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
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.
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.
notebooks/03_visualisation.ipynb.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/finalwhen 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.
Every sheet
| Sheet | Group | Contents | Source |
|---|---|---|---|
| Overview | Orientation | What drives desertion in a volunteer army: the question, the design, the evidence, how it is checked, and where it stands. | reference/OVERVIEW.md |
| Descriptives | Orientation | Live figures and tables computed from data/final. | generated from data/final |
| Data QA | Orientation | Invariant checks on the current build. | generated from data/final |
| Roadmap / to-do | Orientation | What's done and what remains. | reference/ROADMAP.md |
| What's new | Orientation | Site updates + the live-page link. | reference/SITE_CHANGELOG.md |
| Pipeline | How it works | What every script does. | reference/PIPELINE.md |
| Place matching | How it works | Birthplace → 1910 census district. | reference/PLACE_MATCHING.md |
| Digitising the 1910 register | How it works | One 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 route | How 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. | reference/LEXIKON_1910_ROUTE.md |
| 1910 register — the municipal data | How 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. | reference/REGISTER_VARIABLES.md |
| Amateur-theatre measure | How it works | Czech amateur theatre per place and year, 1780–1913: sources, rulings, certain vs possible, where it stands. | reference/THEATRE_MEASURE.md |
| Matice measure | How 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. | reference/MATICE_MEASURE.md |
| Occupation → HISCO | How it works | Czech occupation string → HISCO → social class; §9 summarises the three checks. | reference/OCCUPATION_CODING.md |
| Education ladder | How it works | The 1869 Austrian school ladder — and why the apprentices' continuation school is not a rung on it. | reference/EDUCATION_LADDER.md |
| Rank ladders | How it works | The two rank systems and the reset. | reference/ranks/rank_ladders.md |
| Rank recognition 1918–38 | How it works | Legal memo behind the common scale. | reference/ranks/rank_recognition_1918_1938.md |
| Units & armies | How it works | regiment vs unit_key, and which units Fidler confirms. | generated from data/final |
| Habsburg army & recruitment | How it works | Ergänzung system: how a unit ties to a home district (+ regiment explorer). | reference/units/ERGAENZUNG.md |
| Source provenance | How it works | Which source grounds which mechanism. | generated from data/final |
| Architecture map | How it works | Which script imports which. | generated from data/final |
| Variables | What's in the data | Every constructed variable, with formulas. | reference/VARIABLES.md |
| Data dictionary | What's in the data | Every final column and its source. | data/final/data_dictionary.csv |
| Census codebook | What's in the data | Every census covariate, EN ← DE/HU. | reference/census/census_codebook.csv |
| Data sources | What's in the data | Provenance and licences. | reference/DATA_SOURCES.md |
| References | What's in the data | Literature cited, in APA — hover any citation to preview. | generated from data/final |
| Symbol usage | What's in the data | Every public function and who calls it. | generated from data/final |
| Party register | What's in the data | 55 parties of the Czech lands 1861–1918, with every merger and renaming (Akademický atlas, plate IV.37). | reference/parties/atlas_iv37_parties.csv |
| Unit authority | What's in the data | Legion units → theatre, dates, lineage (Fidler 2025). | reference/units/unit_authority.csv |
| Commits & versions | History & decisions | Machine record: commits, module versions, working-tree state. | generated from data/final |
| Decisions | History & decisions | Register of every judgement call, with a link to the argument. | reference/DECISIONS.md |
| Audits | History & decisions | Three dated code+logic reviews (2026-10-24, 2026-09-22, 2026-07-16): findings, fixes, verification. | reference/AUDIT_2026-10-24.md |
| Validation register | History & decisions | Every check, and what each one is blind to. | reference/VALIDATION.md |
| External validation — units & ranks | History & decisions | Checked against Fidler 2025 — what agreed, what it caught. | reference/VALIDATION_FIDLER.md |
| External validation — occupations | History & decisions | The German bridge, OccCANINE and CZ-ISCO on the Czech→HISCO dictionary. | reference/VALIDATION_OCCUPATIONS.md |
| External validation — education | History & decisions | The 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("..")):
01_data_collection.ipynb— scrapes both person databases intodata/raw/(long-running; resumable) and runs post-scrape QA.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 underdata/final/.03_visualisation.ipynb— descriptive figures and LaTeX tables fromdata/final/only, intooutput/.
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
| module | version | purpose |
|---|---|---|
| utils/docs/build_docs.py | — | generate the single-file HTML documentation site |
| utils/docs/build_fonts.py | 1.0 | Subset the Libertinus faces the docs site embeds -> utils/docs/fonts/*.woff. |
| utils/lexikon_1910/bakeoff.py | 1.3.0 | The bake-off, as a module: the configuration and the run loop, in one place. |
| utils/lexikon_1910/build_contrast_sheet.py | 1.3.0 | The contrast sheet: the look-alike symbols the readers swap, side by side at printed size (D-99). |
| utils/lexikon_1910/build_de_page_map.py | 1.0.0 | Pair 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.py | 0.1.0 | Cut the docs-site figure out of the 1910 register — images plus derived geometry. |
| utils/lexikon_1910/build_mark_ledger.py | 1.7.0 | Fill the mark ledger (D-109) from what the r5 gold run left on disk, and report it. |
| utils/lexikon_1910/build_register_walkthrough.py | 1.0.0 | The 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.py | 1.2.0 | Cut 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.py | 0.5.0 | Podrobný seznam míst / Spezialortsrepertorium 1910 — grid, blocks, invariants. |
| utils/lexikon_1910/lexikon_1910_attributes.py | 0.1.0 | What the register prints *beside* two glyphs: a school's classes and language, |
| utils/lexikon_1910/lexikon_1910_census.py | 0.2.0 | Reconcile transcribed 1910 register blocks against the okres-level census. |
| utils/lexikon_1910/lexikon_1910_courts.py | 0.1.0 | The court each section of the register belongs to: one 1910 backbone court per heading (D-122). |
| utils/lexikon_1910/lexikon_1910_crosswalk.py | 1.0.0 | lexikon_1910_crosswalk — the German Böhmen volume's places paired with the Czech volume's (D-150) |
| utils/lexikon_1910/lexikon_1910_de.py | 0.1.0 | Spezialortsrepertorium Böhmen 1910 — the German twin of the Czech volume. |
| utils/lexikon_1910/lexikon_1910_de_notes.py | 0.5.0 | The German edition's apparatus, read from the image alone (D-92). |
| utils/lexikon_1910/lexikon_1910_de_table.py | 0.1.0 | The German volumes' table, cut into blocks from the image and read by a model (D-160). |
| utils/lexikon_1910/lexikon_1910_editions.py | 0.2.0 | The two editions of the 1910 register as two measurements of one truth (D-58, D-92). |
| utils/lexikon_1910/lexikon_1910_image.py | 0.3.0 | Page geometry of the 1910 register from the rendered page alone — no text layer. |
| utils/lexikon_1910/lexikon_1910_institutions.py | 0.15.0 | Decode the ``Úřady, ústavy`` footnote into an institution inventory. |
| utils/lexikon_1910/lexikon_1910_ledger.py | 1.2.0 | The history of every mark of the 1910 register through the reading route (D-109). |
| utils/lexikon_1910/lexikon_1910_names.py | 0.6.0 | Anchor 1910 register blocks to known municipalities, and let the volume name itself. |
| utils/lexikon_1910/lexikon_1910_notes.py | 0.11.0 | The footnote apparatus printed below the 1910 register table. |
| utils/lexikon_1910/lexikon_1910_pages.py | 0.1.0 | Read a whole page of the 1910 register with a cloud model, and price the run first. |
| utils/lexikon_1910/lexikon_1910_register.py | 1.7.0 | lexikon_1910_register — the 1910 register's frozen-run output as analysis-ready municipal data |
| utils/lexikon_1910/lexikon_1910_vision.py | 0.5.0 | Read the below-table apparatus from the *image*, and hand the result to the |
| utils/lexikon_1910/lexikon_1910_volume.py | 0.2.0 | One profile per volume of the 1910 register — what differs from book to book. |
| utils/lexikon_1910/locate_glyphs.py | 1.1.0 | E3, 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.py | 1.1.0 | Build the gold worksheets for the German edition (Spezialortsrepertorium Böhmen). |
| utils/lexikon_1910/read_transcription_md.py | 1.1.0 | Read the whole-page gold sheets, and grade the transcriber while doing it. |
| utils/lexikon_1910/reader_batches.py | 1.5.0 | The readers, batched: queued requests in, cached replies out (D-108's prices). |
| utils/lexikon_1910/report_unlisted_abbreviations.py | 1.0.0 | The offices items no code in ``institution_abbreviations.csv`` reaches, on one run's readings (D-156). |
| utils/lexikon_1910/rescore_passes.py | 1.4.0 | Re-score every cached symbol pass at the analysis unit. No model call, no spend. |
| utils/lexikon_1910/run_de_table.py | 0.2.0 | The 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.py | 1.2.1 | E1 — is the gap between the two cloud pipelines the model, or the view? |
| utils/lexikon_1910/run_e1b_second_reader.py | 1.3.0 | E1b — which cheap reader of another family errs somewhere else than Gemini? |
| utils/lexikon_1910/run_e3_pages.py | 0.1.0 | E3 on any page: the locator and DINOv3 on the readers' saved section crops (D-112). |
| utils/lexikon_1910/run_frozen.py | 0.21.0 | The 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.py | 1.2.0 | E3, 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.py | 1.4.0 | The 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.py | 1.12.0 | Test B — does one Opus verify call settle what Gemini and Haiku dispute? (D-72, D-89, D-98) |
| utils/lexikon_1910/score_certification.py | 1.6.0 | Score the sealed certification pages — once, against metrics written down first. |
| utils/lexikon_1910/score_r5_german_notes.py | 1.1.0 | Score 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.py | 1.1.0 | The readers' section crops, saved as images for every page (D-112, step 3 beyond the gold). |
| utils/linkage.py | 1.3.0 | Cross-dataset person linkage (legionnaires ↔ Habsburg fallen) |
| utils/parsing/affiliations.py | 2.3.0 | political affiliation, on the atlas's own genealogy |
| utils/parsing/amateur_theatre.py | 0.6.1 | is the amateur-theatre database usable as a pre-1914 covariate? |
| utils/parsing/amateur_theatre_model.py | 0.1.0 | The amateur-theatre model pass: the chronicle clauses the rules leave open, read by a model. |
| utils/parsing/census.py | 1.3.0 | 1910/1911 Cisleithanian census loader, joined to the district backbone |
| utils/parsing/census_hu.py | 1.1.0 | 1910 Transleithanian (Upper-Hungary) járás-level census panel |
| utils/parsing/census_labels.py | 1.3.0 | English column names + derived shares for the 1910 census panels |
| utils/parsing/clean.py | 1.0.1 | Generic raw-field cleaning |
| utils/parsing/dates.py | 1.2.0 | Date column cleaning |
| utils/parsing/districts.py | 1.2.0 | 1910 administrative-district backbone (okres + soudní okres), in Czech |
| utils/parsing/districts_hu.py | 1.0.0 | 1910 Transleithanian (Upper-Hungary) district backbone, Magyar + Slovak |
| utils/parsing/education.py | 2.1.0 | the Austrian school ladder, parsed rather than classified |
| utils/parsing/exit_codes.py | 1.2.0 | Exit code harmonisation for the 'Konec v legiích' field. |
| utils/parsing/geonames.py | 1.0.0 | named places from the ČÚZK Geonames database (Databáze geografických jmen ČR) |
| utils/parsing/habsburg.py | 1.2.0 | VÚA fallen-soldier field parsing |
| utils/parsing/lexikon.py | 1.7.0 | settlement crosswalk from the Historický lexikon obcí ČR 1869–2011 |
| utils/parsing/lexikon_sk.py | 1.1.0 | Slovak settlement crosswalk + 1910 ethnicity from the Majo (2012) lexikón |
| utils/parsing/locations.py | 1.1.0 | Place-name decomposition (municipality / okres / land) |
| utils/parsing/locative.py | 0.4.0 | Czech place names printed in the locative (*v Brně*, *ve Vodňanech*, *v Hradci Králové*) |
| utils/parsing/matice.py | 0.8.0 | Matice česká and Matice moravská member registers — OCR to one row per printed entry. |
| utils/parsing/occupations.py | 1.4.0 | HISCO / HISCLASS coding of the free-text ``Zaměstnání`` field |
| utils/parsing/prng.py | 1.0.0 | Polish localities from the PRNG (Państwowy Rejestr Nazw Geograficznych), Těšín Silesia |
| utils/parsing/provenance.py | 1.0.0 | what source csol.cz cites for a legionnaire record |
| utils/parsing/ranks.py | 1.4.3 | Austro-Hungarian (Habsburg) **and** Czechoslovak Legion rank standardisation |
| utils/parsing/records_geo.py | 1.9.0 | attach the 1910 political-okres id to person records |
| utils/parsing/records_geo_hu.py | 1.4.0 | attach the 1910 Transleithanian járás/county to Slovak-born records |
| utils/parsing/religion.py | 1.0.0 | English gloss for the ``religion`` value (legionnaire dataset) |
| utils/parsing/ruian.py | 1.2.0 | today's official geography (ČÚZK RÚIAN) and the 1910 settlement crosswalk onto it |
| utils/parsing/schema.py | 1.13.0 | Canonical English schema for the two final datasets |
| utils/parsing/spatial.py | 1.1.0 | point-in-polygon court-district assignment from the MPIDR 1910 polygons |
| utils/parsing/theater.py | 1.1.0 | Theatre-of-service classifier (legionnaire dataset) |
| utils/parsing/units.py | 1.3.0 | Regiment / unit name parsing |
| utils/parsing/vanished.py | 1.0.0 | vanished and abandoned settlements of the Czech lands, from Wikidata |
| utils/parsing/vua_courts.py | 0.1.0 | the VÚA inventories of three Habsburg military courts, as tables |
| utils/parsing/wikidata.py | 1.4.0 | coordinates + QIDs for Czech settlements (enriches the lexikon crosswalk) |
| utils/plotting/th_style_v113.py | 1.2.0 | th_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.py | 1.0.0 | Build the page index for Österreichische Statistik XCIII/1 (Unterrichtsanstalten 1908/09). |
| utils/reference_tools/education/check_unterricht_transcription.py | 1.0.0 | Invariants on the transcription of Österreichische Statistik XCIII/1 (1908/09). |
| utils/reference_tools/education/draw_education_sample.py | 1.0.0 | Draw the `Vzdělání` strings a person will code by hand, reproducibly. |
| utils/reference_tools/education/run_unterricht_check.py | 1.0.0 | Is the education ladder the one the Austrian state actually operated? |
| utils/reference_tools/education/score_education_gold.py | 1.0.0 | Score a hand-coded education worksheet against `education.py`. |
| utils/reference_tools/occupations/build_conflict_workbook.py | 2.1.1 | every unresolved occupation coding, as one adjudicable table |
| utils/reference_tools/occupations/build_czisco_bridge.py | 1.0.0 | generate reference/occupations/czisco_authority.csv |
| utils/reference_tools/occupations/build_hisco_authority.py | 1.1.0 | generate reference/occupations/hisco_authority.csv |
| utils/reference_tools/occupations/build_occupation_curation.py | 1.0.0 | generate/refresh reference/occupations/occupation_hisco_curation.csv |
| utils/reference_tools/occupations/czech_glosses.py | 1.2.0 | Czech occupational key -> German / English gloss |
| utils/reference_tools/occupations/download_hisco_sources.py | 1.0.0 | fetch the HISCO authority tables into data/raw/hisco/ |
| utils/reference_tools/occupations/run_census_berufsstellung_check.py | 1.0.0 | is a `rolník` a farm *owner* or a farm *worker*? |
| utils/reference_tools/occupations/run_czisco_check.py | 1.0.0 | CZ-ISCO as a third coder, on the Czech side |
| utils/reference_tools/occupations/run_link_bridge.py | 1.2.0 | resolve the Czech glosses to HISCO codes via the German link table |
| utils/reference_tools/occupations/run_mosaic_beruf_check.py | 1.0.0 | does the German word for a farmer mean an *owner*, one record at a time? |
| utils/reference_tools/occupations/run_occcanine_check.py | 1.1.0 | independent second coder for the Czech occupation dictionary |
| utils/reference_tools/parties/check_atlas_parties.py | 1.0.0 | invariants on the transcribed IV.37 party register |
| utils/reference_tools/places/audit_full_run_pages.py | 0.1.0 | Every 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.py | 0.1.0 | A Token Review queue for clicking every pictogram of the German gold pages. |
| utils/reference_tools/places/build_german_click_strips.py | 0.1.0 | Cut 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.py | 0.1.0 | Sheets 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.py | 0.1.0 | Which candidate-step filter removes the German glyphs the author clicked? |
| utils/reference_tools/places/german_click_recall.py | 0.1.0 | How well does the locator's box land on the glyph the author clicked? (German gold, offices.) |
| utils/reference_tools/places/german_locator_trial.py | 0.1.0 | Does a locator trained on the German gold place German claims better than the Czech one? |
| utils/reference_tools/places/german_supervised_placement.py | 0.1.0 | Does a glyph classifier trained on the author's clicks place German claims on the right glyph? |
| utils/reference_tools/places/map_intensities.py | 0.2.0 | The intensities of ``run_intensities.py`` side by side, one figure per level. |
| utils/reference_tools/places/map_matice.py | 0.3.0 | Maps 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.py | 0.1.0 | Detect by naming: let the bank find the German glyphs, instead of a locator guessing where they are. |
| utils/reference_tools/places/run_german_oracle.py | 0.1.0 | The oracle test: does the Czech-built bank name German glyphs when the box is certainly right? |
| utils/reference_tools/places/run_german_reader_boxes.py | 0.2.0 | The reader gives the box: can DINOv3 check German claims on the boxes Gemini itself returns? |
| utils/reference_tools/places/run_intensities.py | 0.6.0 | Four 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.py | 1.0.0 | Score the text abbreviations on the sealed certification pages — a second metric, fixed before it was run. |
| utils/reference_tools/places/statutory_institutions.py | 0.5.0 | The statutory cities' institutions, from their verified prose: stage 2 of D-178. |
| utils/reference_tools/places/statutory_prose.py | 0.3.0 | The 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.py | 1.0.0 | generate reference/units/unit_timeline.csv |
| utils/reference_tools/units/check_unit_timeline.py | 1.0.0 | invariants 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.py | 0.1.0 | the three VÚA military-court inventories, fetched once |
| utils/scraping/progress.py | 1.0.0 | Progress-bar helper shared by the Kramerius downloaders. |
| utils/scraping/run_lexicon_downloads.py | 1.0.0 | Download the two outstanding 1910 *Spezialortsrepertorium* volumes into |
| utils/scraping/run_matice_downloads.py | 1.2.0 | Download the digitised member lists of two Czech national societies, the *Matice |
| utils/scraping/run_oeulk_downloads.py | 1.0.0 | Download every volume of *Österreich-Ungarns letzter Krieg 1914–1918* from the |
| utils/scraping/scraper_amaterskedivadlo.py | 0.2.2 | amaterskedivadlo.cz scraper — importable module + CLI. **Feasibility pilot.** |
| utils/scraping/scraper_csol.py | — | csol.cz legionnaire scraper — importable module + CLI. |
| utils/scraping/scraper_kramerius.py | 1.4.0 | Kramerius v5 downloader — importable module + CLI. |
| utils/scraping/scraper_kramerius7.py | 1.3.0 | Kramerius v7 downloader — importable module + CLI. |
| utils/scraping/scraper_verlustlisten.py | 0.1.0 | Verlustlisten Ö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.py | 1.2.0 | LaTeX table generation utilities. |
| utils/validation/build_validation_workbook.py | — | human-validation sample for place matching + rank parsing |
| utils/validation/check_ostafin_silesia.py | 0.1.0 | Austrian Silesia 1910 against Ostafin et al. (2020): an independent transcription of our source. |
| utils/validation/data_checks.py | 1.16.1 | the invariants ``data/final`` must satisfy |
| utils/validation/lexikon_population.py | 0.1.0 | The 1910 population of the ČSÚ *Historický lexikon obcí ČR 1869–2005*, díl I, and its notes. |
| utils/validation/run_completeness.py | 0.1.0 | Whether a frozen run has every reply it asked for, before anything is built on it. |
| utils/validation/score_sealed_retest.py | 0.1.0 | The 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
| File | Unit | Rows | Columns |
|---|---|---|---|
| persons/legionnaires_clean | legionnaire | 132,993 | 218 |
| persons/habsburg_clean | fallen Habsburg soldier | 186,192 | 102 |
| census/census_okres_1910 | 1910 political okres | 158 | 509 |
| census/census_okres_1910_pooled | 1910 okres, city/rural twins pooled | 148 | 510 |
| census/census_hu_jaras_1910 | 1910 járás / town | 192 | 90 |
| geo/districts_1910 | court district | 339 | 11 |
| geo/districts_hu_1910 | járás / town | 192 | 12 |
| geo/settlements_1910 | Czech settlement | 18,916 | 18 |
| geo/settlements_hu_1910 | Slovak obec | 3,511 | 21 |
| persons/overlap_matches | confirmed overlap | 1,340 | 15 |
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.
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.
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.
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.
Outcome — exit from the Legions
| Code | Meaning | n | % of records |
|---|---|---|---|
| B | Killed in action | 2,046 | 1.5% |
| ČSA | Served in post-1918 Czechoslovak army | 2,987 | 2.2% |
| D | Deserted | 3,717 | 2.8% |
| DM | Demobilised | 61,505 | 46.2% |
| N | Missing | 645 | 0.5% |
| NS | Unfit for service | 1,448 | 1.1% |
| P | Executed | 93 | 0.1% |
| S | Died | 2,080 | 1.6% |
| V | Returned home | 16,896 | 12.7% |
| X | See note | 17,530 | 13.2% |
| Z | Died of wounds | 524 | 0.4% |
| — | No code recorded (censored) | 30,770 | 23.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
| Theatre | Legionnaires | Deserted | Desertion rate |
|---|---|---|---|
| Russia | 87,734 | 3,607 | 4.11% |
| Italy | 28,620 | 66 | 0.23% |
| France | 10,627 | 5 | 0.05% |
| Unknown | 4,666 | 26 | 0.56% |
| Serbia/Balkans | 886 | 11 | 1.24% |
| Britain/Dominions | 433 | 2 | 0.46% |
| Post-war (ČSA) | 27 | 0 | 0.00% |
Rank reset (headline regressor) ⇱ Interactive ↕ Hover for values
| Measure | N resolved | Demoted | Unchanged | Promoted | Mean |
|---|---|---|---|---|---|
| Fine (ALT-1 common scale) | 91,561 | 18.9% | 77.2% | 3.9% | -0.45 |
| Coarse (NATO OF/OR band) | 91,561 | 18.7% | 77.4% | 3.9% | -0.58 |
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).
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.
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.
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.
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 inhabitant | per Czech speaker | per legionnaire | |||||
|---|---|---|---|---|---|---|---|
| Matice | Theatre | Legionnaires | Matice | Theatre | Legionnaires | Desertion | |
| 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 inhabitant | per Czech speaker | per legionnaire | |||||
|---|---|---|---|---|---|---|---|
| Matice | Theatre | Legionnaires | Matice | Theatre | Legionnaires | Desertion | |
| 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
| Dataset | Rows | Czech okres_id | Slovak jaras_id | Court district | Coordinates |
|---|---|---|---|---|---|
| Legionnaires | 132,993 | 101,274 (76.1%) | 11,236 (8.4%) | 58,000 | 58,074 |
| Habsburg fallen | 186,192 | 122,552 (65.8%) | 21,539 (11.6%) | 68,381 | 68,450 |
| Czech tier | Legionnaires | Habsburg fallen |
|---|---|---|
| okres | 93287 | 112360 |
| court | 4618 | 1462 |
| fuzzy | 2470 | 1919 |
| alias | 36 | 16 |
| settlement | 611 | 1363 |
| muni | 252 | 5432 |
| Slovak tier | Legionnaires | Habsburg fallen |
|---|---|---|
| jaras | 9182 | 18131 |
| town | 862 | 1856 |
| megye | 624 | 43 |
| fuzzy | 897 | 528 |
| settlement | 295 | 1024 |
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
| Confidence | Evidence | Pairs |
|---|---|---|
| high | Exact full date of birth + matching name | 1201 |
| medium | VÚA legion note + birth okres + year + name | 139 |
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
| Variable | Column | Legionnaires | Habsburg fallen |
|---|---|---|---|
| Date of birth | birth_date | 96.3% | 93.0% |
| Year of birth | birth_year | 96.3% | 93.0% |
| Birth district (raw) | birth_okres | 91.1% | 75.0% |
| Education | education | 79.0% | — |
| Occupation | occupation | 89.4% | — |
| Religion | religion | 29.7% | — |
| Habsburg rank at capture | habsburg_rank | 74.2% | — |
| Legion rank at enrolment | legion_rank | 85.9% | — |
| Enrolment date | enroll_date | 90.9% | — |
| Exit date | exit_date | 79.3% | — |
| Date of death | death_date | 3.3% | 95.8% |
| Cause of death | cause | — | 95.4% |
Figures
Generated by notebooks/03_visualisation.ipynb; PDF and beamer variants of each live in output/figures/.
Legionnaires
output/figures/descriptives/affiliation/affiliation_russia.pngoutput/figures/descriptives/affiliation/desertion_by_affiliation.pngoutput/figures/descriptives/composition/educ_by_theatre.pngoutput/figures/descriptives/occupation/desertion_by_hisclass.pngoutput/figures/descriptives/occupation/occupation_hisclass_composition.pngoutput/figures/descriptives/rank/rank_reset_hist.pngoutput/figures/descriptives/theater/desertions_by_theatre.pngoutput/figures/descriptives/theater/enrollment_by_theatre.pngoutput/figures/descriptives/theater/russia_enrol_vs_desert.pngHabsburg fallen
output/figures/descriptives_habsburg/causes/cause_breakdown.pngoutput/figures/descriptives_habsburg/composition/birth_death_year.pngoutput/figures/descriptives_habsburg/composition/top_birth_okres.pngoutput/figures/descriptives_habsburg/theater/deaths_by_theatre.pngoutput/figures/descriptives_habsburg/theater/theatre_composition.pngfigures
output/figures/intensity/intensity_maps_court.pngoutput/figures/intensity/intensity_maps_municipal.pngoutput/figures/intensity/intensity_maps_okres.pngoutput/figures/matice/matice_map_court.pngoutput/figures/matice/matice_map_municipal.pngoutput/figures/matice/matice_map_okres.pngData 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.
| Check | If it fails | |
|---|---|---|
| PASS | Legionnaires: data/final carries every column the current code emits | 0 column(s) missing — data/final predates the code; re-run notebook 02: [] |
| PASS | Habsburg fallen: data/final carries every column the current code emits | 0 column(s) missing — data/final predates the code; re-run notebook 02: [] |
| PASS | Legionnaires · birth: placed in at most one 1910 frame | 0 record(s) hold both a Czech okres_id and a Slovak jaras_id |
| PASS | Legionnaires · birth: no Czech attributes on Slovak-placed records | 0 record(s) leak cross-frame attributes |
| PASS | Legionnaires · home: placed in at most one 1910 frame | 0 record(s) hold both a Czech okres_id and a Slovak jaras_id |
| PASS | Legionnaires · home: no Czech attributes on Slovak-placed records | 0 record(s) leak cross-frame attributes |
| PASS | Habsburg fallen · birth: placed in at most one 1910 frame | 0 record(s) hold both a Czech okres_id and a Slovak jaras_id |
| PASS | Habsburg fallen · birth: no Czech attributes on Slovak-placed records | 0 record(s) leak cross-frame attributes |
| PASS | Habsburg fallen · home: placed in at most one 1910 frame | 0 record(s) hold both a Czech okres_id and a Slovak jaras_id |
| PASS | Habsburg fallen · home: no Czech attributes on Slovak-placed records | 0 record(s) leak cross-frame attributes |
| PASS | Legionnaire primary key unique | duplicate legion_id values |
| PASS | Habsburg primary key unique | duplicate vua_id values |
| PASS | Missing values do not fabricate an exit code | a missing/placeholder cell parses to a non-empty code set |
| PASS | exit_N plausible (= 645, not ~27k) | exit_N looks fabricated from empty cells — see audit A3 |
| PASS | Provenance parser self-test | 0 provenance behaviour(s) regressed: [] |
| PASS | source_spis is set exactly where a source is recorded | 0 row(s) where the flag and record_source disagree |
| PASS | source_karta is set exactly where a source is recorded | 0 row(s) where the flag and record_source disagree |
| PASS | source_extra is set exactly where a source is recorded | 0 row(s) where the flag and record_source disagree |
| PASS | source_extra_ref present exactly where source_extra is True | 0 row(s) disagree |
| PASS | No personnel-document name left in the extra citation | 0 extra citation(s) still contain a document name |
| PASS | Affiliation parser self-test | 0 affiliation behaviour(s) regressed: [] |
| FAIL | Every canonical in PARTY_ORDER has a dummy column | missing: ['cns'] |
| PASS | pol_* dummies are <NA> exactly where pol_recorded is False | 0 flag/row disagreement(s) across 23 dummies |
| PASS | pol_atlas_codes is <NA> on unrecorded rows | 0 unrecorded row(s) carry atlas codes |
| PASS | education_ord within 0..5 | an education_ord falls outside the six-rung ladder |
| PASS | education label and education_ord agree | 0 record(s) where the label is not EDUC_ORDER[education_ord] |
| PASS | Apprentice flag never leaves a record below Elementary | 0 record(s) flagged apprentice sit below rung 1 |
| PASS | A missing education fabricates no companion value | columns filled where `education` is missing: {} |
| PASS | No completion claimed for a vocational course of unknown length | 0 vocational record(s) carry a completion verdict |
| PASS | education_top's own rung equals education_ord | 0 record(s) disagree; 0 name an unknown institution |
| PASS | TIE_BREAK covers every institution exactly once | TIE_BREAK and INSTITUTIONS disagree — the tie-break is not total and ties fall back on string order |
| PASS | education_top is named in education_institutions unless it is imputed | 0 row(s) where educ_top is absent and not flagged |
| PASS | Education parser self-test | 0 education behaviour(s) regressed: [] |
| PASS | Missing occupation does not fabricate a HISCO code | a missing/empty cell codes to a real occupation |
| PASS | Occupation matcher self-test | 0 matcher behaviour(s) regressed: [] |
| PASS | No HISCO output on empty occupation cells | 0 column(s) non-null where occupation is <NA>: [] |
| PASS | occupation coding coverage (98.5% of non-empty) | only 117,140/118,949 coded — the curation has regressed |
| PASS | Every occupation HISCO code exists in the authority | 0 code(s) absent from hisco_authority.csv: [] |
| PASS | occupation HISCLASS within 1..13 | a HISCLASS value falls outside the documented range |
| PASS | occupation HISCLASS_5 within 1..5 | a HISCLASS_5 value falls outside 1..5 |
| PASS | Every coded occupation carries a HISCO major group | 0 record(s) hold a HISCO code but no major group |
| PASS | occupation: no social class on a HISCO sentinel | a HISCO sentinel carries a stratification scale: {} |
| PASS | Every occupation HISCO status code is real | status value(s) absent from the HISCO code list: [] |
| PASS | Every occupation HISCO relation code is real | relation value(s) absent from the HISCO code list: [] |
| PASS | No HISCO output on empty occupation_interwar cells | 0 column(s) non-null where occupation_interwar is <NA>: [] |
| PASS | occupation_interwar coding coverage (84.6% of non-empty) | only 4,299/5,080 coded — the curation has regressed |
| PASS | Every occupation_interwar HISCO code exists in the authority | 0 code(s) absent from hisco_authority.csv: [] |
| PASS | occupation_interwar HISCLASS within 1..13 | a HISCLASS value falls outside the documented range |
| PASS | occupation_interwar HISCLASS_5 within 1..5 | a HISCLASS_5 value falls outside 1..5 |
| PASS | occupation_interwar: no social class on a HISCO sentinel | a HISCO sentinel carries a stratification scale: {} |
| PASS | occupation_hisclass_high is the better of the two | 0 row(s) where the convenience column disagrees |
| PASS | occupation_career_stage_ord is the map of the label | 0 row(s) where the ordinal disagrees with the stage |
| PASS | Every career stage is a documented rung | stage value(s) absent from CAREER_STAGE_ORD: [] |
| PASS | No qualifier on a dual match | 0 dual row(s) carry a qualifier — the second occupation is being read as a modifier |
| PASS | Qualifier is never the whole occupation string | 0 row(s) where the qualifier swallowed the head noun |
| PASS | illegitimacy_rate built | derived rate missing from the census panel |
| PASS | pop_total has no zero rows | an all-missing okres summed to 0 instead of NA |
| PASS | Census pooled to the twin unit (148 rows, 10 pooled) | 148 pooled rows vs 158 okresy — pairs not collapsed |
| PASS | Pooled panel has no missing language share | 0 pooled unit(s) still <NA> — the pair total was lost, not merged |
| PASS | Pooled shares recomputed from pooled counts | 0 pooled share(s) do not equal counts/total — averaged, not rebuilt |
| PASS | Legionnaires · birth: twin role set exactly on the twin population | 0 record(s) disagree between birth_okres_id and birth_okres_twin |
| PASS | Habsburg fallen · birth: twin role set exactly on the twin population | 0 record(s) disagree between birth_okres_id and birth_okres_twin |
| PASS | Legionnaires · birth: Prague tag only on Prague-area okresy | 0 record(s) tagged birth_okres_prague sit outside okres Praha and its suburb destinations |
| PASS | Legionnaires · birth: every 'suburb' record actually left okres Praha (1,468 moved) | 0 record(s) tagged 'suburb' are still in okres Praha |
| PASS | Legionnaires · birth: okres name matches okres id | 0 record(s) carry a district label that is not their id's |
| PASS | Legionnaires · birth: court snap flag present exactly where a court is | 0 record(s) have a court without a flag, or the reverse |
| PASS | Habsburg fallen · birth: Prague tag only on Prague-area okresy | 0 record(s) tagged birth_okres_prague sit outside okres Praha and its suburb destinations |
| PASS | Habsburg fallen · birth: every 'suburb' record actually left okres Praha (69 moved) | 0 record(s) tagged 'suburb' are still in okres Praha |
| PASS | Habsburg fallen · birth: okres name matches okres id | 0 record(s) carry a district label that is not their id's |
| PASS | Habsburg fallen · birth: court snap flag present exactly where a court is | 0 record(s) have a court without a flag, or the reverse |
| PASS | Chartered towns sit in their own unit, not a járás | 0 obec row(s) named after a chartered town of their own county are filed on a járás — suspect an unmatched section header |
| PASS | Obec 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) |
| PASS | Legionnaires · birth: járás twin role set exactly on the twin population | 0 record(s) disagree between birth_jaras_id and birth_jaras_twin |
| PASS | Habsburg fallen · birth: járás twin role set exactly on the twin population | 0 record(s) disagree between birth_jaras_id and birth_jaras_twin |
| PASS | legionnaires_census_1910: census join does not duplicate rows | 132,993 rows vs 132,993 persons — join blowup |
| PASS | habsburg_census_1910: census join does not duplicate rows | 186,192 rows vs 186,192 persons — join blowup |
| PASS | Every rank label resolves to itself | 0 label(s) resolve elsewhere, e.g. [] |
| PASS | No general scores below the OF-6 band | 0 record(s) name a general but score as a lower grade |
| PASS | unit_key present wherever a regiment is | 0 record(s) have a regiment but no unit_key |
| PASS | Habsburg note parser self-test | 0 behaviour(s) regressed: [] |
| PASS | Theatre classifier self-test | 0 behaviour(s) regressed: [] |
| PASS | Linkage name test self-test | 0 behaviour(s) regressed: [] |
| PASS | Slovak district matcher self-test | 0 behaviour(s) regressed: [] |
| PASS | Legionnaires · birth: every járás-placed record has land Slovakia | 0 record(s) sit in a Slovak járás with another land |
| PASS | Legionnaires · home: every járás-placed record has land Slovakia | 0 record(s) sit in a Slovak járás with another land |
| PASS | Habsburg fallen · birth: every járás-placed record has land Slovakia | 0 record(s) sit in a Slovak járás with another land |
| PASS | Habsburg fallen · home: every járás-placed record has land Slovakia | 0 record(s) sit in a Slovak járás with another land |
| PASS | Habsburg fallen · birth_cisleithanian agrees with birth_land | 0 record(s) where the flag contradicts the land |
| PASS | Habsburg fallen · home_cisleithanian agrees with home_land | 0 record(s) where the flag contradicts the land |
| PASS | Linkage is one-to-one (no id matched twice on either side) | 0 surplus link(s) |
| PASS | No linked pair has two full dates of birth that disagree | 0 linked pair(s) with conflicting full DOBs |
| PASS | Every linked pair clears the surname floor | 0 linked pair(s) with a surname below the floor |
| PASS | Legion-side and VÚA-side links agree | 0 legion link(s) the VÚA side does not return |
| PASS | exit_date within 1914–1939 | 0 date(s) outside the range |
| PASS | exit_date_precision present exactly where exit_date is | 0 row(s) disagree |
| PASS | capture_date within 1914–1918 | 0 date(s) outside the range |
| PASS | capture_date_precision present exactly where capture_date is | 0 row(s) disagree |
| PASS | enroll_date within 1914–1922 | 0 date(s) outside the range |
| PASS | enroll_date_precision present exactly where enroll_date is | 0 row(s) disagree |
| PASS | Exit before enrolment, day-precise dates (246 — source contradictions) soft | exit dates before enrolment have grown — look at the parse |
| PASS | Every ladder rank has a common grade | rank(s) with no common grade: [] |
| PASS | Legionnaires: every column is in the data dictionary | 0 undocumented column(s): [] |
| PASS | Habsburg fallen: every column is in the data dictionary | 0 undocumented column(s): [] |
| PASS | Census codebook (cis) documents every panel column | 0 undocumented column(s): [] |
| PASS | Census codebook (hu) documents every panel column | 0 undocumented column(s): [] |
| PASS | Settlements 1910: key ['settlement_id'] unique and never missing | 0 row(s) with a missing key, 0 duplicate key(s) |
| PASS | Current geography · ruian_obce: key ['obec_kod'] unique and never missing | 0 row(s) with a missing key, 0 duplicate key(s) |
| PASS | Current geography · ruian_casti_obce: key ['cast_kod'] unique and never missing | 0 row(s) with a missing key, 0 duplicate key(s) |
| PASS | Current geography · ruian_ku: key ['ku_kod'] unique and never missing | 0 row(s) with a missing key, 0 duplicate key(s) |
| PASS | Current geography · ruian_zsj: key ['zsj_kod'] unique and never missing | 0 row(s) with a missing key, 0 duplicate key(s) |
| PASS | Current geography · ruian_obce_adjacency: key ['obec_kod', 'neighbour_kod'] unique and never missing | 0 row(s) with a missing key, 0 duplicate key(s) |
| PASS | Current geography · geonames_places: key ['geonames_id'] unique and never missing | 0 row(s) with a missing key, 0 duplicate key(s) |
| PASS | Current geography · prng_places: key ['prng_id'] unique and never missing | 0 row(s) with a missing key, 0 duplicate key(s) |
| PASS | Current geography · vanished_places: key ['qid'] unique and never missing | 0 row(s) with a missing key, 0 duplicate key(s) |
| PASS | Current geography · settlements_1910_geo: key ['settlement_id'] unique and never missing | 0 row(s) with a missing key, 0 duplicate key(s) |
| PASS | Crosswalk: one row per settlements_1910 row, no more, no fewer | 18916 crosswalk rows vs 18916 settlements — rebuild §3e after §3c |
| PASS | Crosswalk: every modern obec_kod is in ruian_obce | 0 row(s) point at no obec |
| PASS | Obec adjacency is symmetric | a neighbour pair is recorded in one direction only |
| PASS | Crosswalk: Wikidata points kept, or replaced only with wd_overridden | 0 Wikidata point(s) replaced without the flag |
| PASS | Crosswalk: every point carries coord_source and coord_ref | 0 point(s) without a source or a citation |
| PASS | Crosswalk: every vanished-place point is in vanished_places | 0 point(s) cite a QID the table lacks |
| PASS | Crosswalk: neighbour matches within 5 km of their 1910 okres | 0 neighbour match(es) beyond the cap |
| PASS | Crosswalk built after settlements_1910 soft | settlements_1910 was rewritten after the crosswalk — re-run notebook 02 §3e |
| PASS | Crosswalk points agree with Wikidata (median 8 m) soft | median distance above 50 m — suspect the S-JTSK transform |
| PASS | Crosswalk points lie in the 1910 register's court (91.9% of 221) soft | below 85 % — the crosswalk or the register's anchoring has drifted; read the in_register_court == False rows |
| PASS | Theatre agrees with Fidler (2025) unit authority (99.6% of 71,854) soft | agreement 99.6% — 266 record(s) place a regiment in a theatre where Fidler says it never existed |
| PASS | Settlement coordinates land in their own okres, before snapping (90.2%) soft | coordinate accuracy below 85% — suspect an unguarded name-match fallback |
Roadmap / to-doOrientation
What's done and what remains. · source: reference/ROADMAP.md
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_keyfor 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_1910has a key (settlement_id); notebook 15 §18 anchors every register municipality and part to it and writesdata/final/register/(places, institutions, cooperatives, one row per municipality), keys asserted in the QA gate. Built today fromcz_certificationas 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.csvfor 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 inabbreviation_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 GermanGB.(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"withREREAD— 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 inLEXIKON_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 inACCEPT_FAILED. Still open on the person side: legionnaires reach the register at court or okres level only (no birthsettlement_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 bareob. 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_groupflags 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.py2.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 settleSLwithout one. It is the Slovenská liga v Amerike (Slovak League of America), an émigré organisation: 188 of the 214 records namingSLor 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, codednar_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,affiliations2.3.0):SLjoinsslov_liga(63 → 211) andČNSis its own dummypol_cns(222), out ofpol_nar_soc(6,541 → 6,319). Notebooks 02 and 03 must be re-run fordata/finaland 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 theDATA_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.csvandensembles.csv): 34,569 ensemble rows with start and end years, a fuzzy191xkept 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 ofsettlements_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 feedsdata/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_theatre0.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 fromchronicle_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 todata/interim/theatre/chronicle_lines.csv; nothing goes todata/final. The database's own abbreviation key is read in as well (2026-10-27,amateur_theatre0.3.0, D-138): saved asdata/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ČOSand 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 throughbirth_qid. Source, citation and rights:DATA_SOURCES.md#22. The treatment is being built (2026-10-28,amateur_theatre0.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(), thenevidence_table()(the ensemble list, the clauses and the first reports in one interval form, with onecompany_idper company), thenplace_year_panel()(7,368 places x 1780-1913, each measure certain and possible), thenarea_year_tables()(summed to 1910 settlement, court and okres). All of it goes todata/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_theatre0.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 rebuiltdata/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 inDATA_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…mm1909ofrun_matice_downloads.pyv1.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 inDATA_SOURCES.md#29. - Both registers are read by rule (2026-10-29,
matice0.1.0, D-158).utils/reference_tools/places/run_matice_pilot.pywritesdata/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, andmatice_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 bypaid_for_year). - The registers are an okres measure now (2026-10-29,
matice0.2.0 +locative0.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.csvholds, per census okres, the founders and members placed there, rates per 1,000 inhabitants and Czech speakers, shrunk rates, posted and local counts apart, andmatice_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 reachesdata/finalor the persons yet. Name variants (locative0.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. Sincelocative0.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.csvcarries the measure per court district (court census 1910 as denominator, the nine statutory-city courts pooled as the census pools them), andutils/reference_tools/places/map_matice.pydraws it by okres, by court and by municipality tooutput/figures/matice/. - Intensity maps, side by side (2026-10-30, D-163, provisional).
utils/reference_tools/places/run_intensities.pywritesdata/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.pydraws them tooutput/figures/intensity/, and the docs site's Descriptives sheet carries them as linked panels, with the definitions fromINTENSITY_MAPS.mdabove 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 (
matice0.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.py1.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_paymentsreads them month by month against each month's printed total,payment_personsgathers a payer's instalments, and the joiners of 1900–1909 are now Bohemia's measure, both lands standing at about 1909. The pilot writesmatice_payments.csv,matice_payment_months.csvandmatice_payers.csvbeside the other tables and prints the checks. A joiner is dated by the first payment (ruled 2026-11-03). Placing improved the same day (locative0.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 (matice0.6.0,amateur_theatre0.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 bysettlement_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 inmatice0.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_z0.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
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}}slotsoverview_facts()fills at build time fromdata/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 byagreement()inrun_occcanine_check.py1.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_zfollow. 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.py0.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,ruianand 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_FAILEDand 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,entryandmarks. -
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 bareopus: 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 fromdata/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 therun_*_downloadsrunners 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 byutils/lexikon_1910/build_register_walkthrough.pyfrom 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.
entryis 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.pyand every count from that run'sfinal_inventory.csv, whoserulecolumn 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 inutils/lexikon_1910/, andreference/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/finalhad 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_exact0.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.295entry_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_tokensand 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 fullob. 5are 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, soeducation_apprenticeis 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šnostis blank on 93,377 of 132,993 records — 70.2 % — and everypol_*dummy was writingFalsethere. That is not "not a Sokol"; it is a source that said nothing, and the difference is the same one that fabricatedexit_Non 26.4k empty cells in the 2026-07-16 audit and thatprovenance.pyalready refuses for its own flags.pol_recorded(39,616 True) marks the base; a record that is filled in but matches no token keepsFalse, 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 setseducation_track(academic vs teacher vs military) andeducation_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 ineducation_years_secondaryon 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.
technikhad been matching inside hydrotechnika andobinside pob. — the unanchored-substring shape thatranks1.4.0 had to remove after Fidler caughtgenerálmajorscoring asmajor. Anchoring it exposed spellings the patterns had never covered:univ.(55 records, where onlyuniver…was matched), thelék.fak./fil.fak.faculty form, andvyš.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:technikandtechnickdiverge 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()andaffiliation_selftest(). They hold the rules a finished frame cannot express — thatreálka obch.akademieandobch.akademie reálkagive the same answer, that the refusal tokensldoes not match insideslov.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. pokris 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 carriednar_demand only lose a spurious second flag, 78 newly gain it, 42 becomevolna_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) andreal.→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.pycan 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, whiledělníkin 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ě. 3is the legal minimum, not an achievement;ob. 8is the same eight years taken in one building; anduč.ústavwas 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.andodb.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.py2.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álkadoes 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 anyvalue_countsis indistinguishable from a missing value — the exact shape of the 2026-07-16 fabricated-exit_Nbug. It isNo schoolingnow. - 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.ADJUDICATEDrecords one verdict per word —keep(130),recoded(41),open(10) — andrun_link_bridge.py1.2.0 applies it last and is the only writer ofreview, so the German bridge can go on flagging genuinely new ambiguity without re-raising anything answered. The new code always lives inHISCO_OVERRIDEand never in the register, so a reader looking a word up finds one answer; the bridge refuses to run if arecodedkey 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 averdictcolumn 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), 57továrníkgo 3 → 1, 51ošetřovatel9 → 4, 28farmaceut4 → 2, 16mlé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_enand 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štaand 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-2sentinel and the flag. The other three are real questions:vlakvedoucí(the guard, currently sharing a code with the engine driver),politik, and the baresprá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_workbook2.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
-2sentinel 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.py1.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íkkept 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.pyrenders 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 fromlibrary/. - 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.czpresents a modern viewer but answers on the v5 API, so it belongs toscraper_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.pykeeps the bar optional: withouttqdmthe downloaders fall back to their old log line and nothing else changes. --namehad 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 asNDK_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.
_stitchwalks 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_resetis 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íkis 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 forsedlá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 reachreference/.
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 inreference/*.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 whenbraš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
exactswallowing 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/finalat 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 markedreviewfor 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.
četnictvowas 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 readoccupation_hiscoalone, andčetnictvoappears 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íkandmalorolníkmove 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íkstays 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íkintorolníkwould 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 vonkovresolves 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 thanokres 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é Moravcescores 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
Ostravawere born in okres Frýdek — the Silesian side, not the Moravian city; 14Nové Městoare 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íchovnames 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 aloneBrno(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: cityX (město), rural bareX, everywhere. That is also what the ČSÚ register means by a bare name — all 118 ring villages around Brno recordokr. 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 trailingvenkovnow 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 andLvov→ 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_idfor the city district, while a bare recordedOlomoucresolves 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) andX (město)(city); Brno alone is named the other way round,Brnofor the city andBrno-venkovfor 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 districtpairs for the selected tier — aggregates over place names, no individual records. This is the part that earns or loses trust:fuzzyturns out to be doing honest work (Ostrava → Moravská Ostrava, Dvůr Králové → Dvůr Králové nad Labem), whilemuniimmediately showsBrno→ 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_idas 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_resetis actually measured on, next to the coarse NATO band it collapses into. - An open question logged, not silently fixed.
svobodníkis 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.bibis 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.bibcannot disagree. The hand-maintainedrank_sources.bibis 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_visualisationagainst the currentdata/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_resetactually 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). --noindexbuild 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_noncombatdecision). - Units & armies sheet (
regimentvsunit_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
| Script | Role | Does |
|---|---|---|
| fetch_vua_courts.py | Scraping | Downloads the name-level archival inventories (*inventáře*) of three H |
| progress.py | Scraping | Progress-bar helper shared by the Kramerius downloaders. |
| run_lexicon_downloads.py | Scraping | Download the two outstanding 1910 *Spezialortsrepertorium* volumes int |
| run_matice_downloads.py | Scraping | Download the digitised member lists of two Czech national societies, t |
| run_oeulk_downloads.py | Scraping | Download every volume of *Österreich-Ungarns letzter Krieg 1914–1918* |
| scraper_amaterskedivadlo.py | Scraping | amaterskedivadlo.cz scraper — importable module + CLI. **Feasibility |
| scraper_csol.py | Scraping | csol.cz legionnaire scraper — importable module + CLI. |
| scraper_kramerius.py | Scraping | Kramerius v5 downloader — importable module + CLI. |
| scraper_kramerius7.py | Scraping | Kramerius v7 downloader — importable module + CLI. |
| scraper_verlustlisten.py | Scraping | Verlustlisten Österreich-Ungarns 1914–1919 (DES index) scraper — impor |
| scraper_vuapraha.py | Scraping | vuapraha.cz scraper — importable module + CLI. |
| affiliations.py | Person parsing | Parses the free-text ``Politická příslušnost`` field into per-party bo |
| clean.py | Person parsing | First-pass normalisation of scraped archival records, applied **before |
| dates.py | Person parsing | Parses Czech date strings across four standalone date columns spanning |
| education.py | Person parsing | ``Vzdělání`` is **not a level**. It is an *itinerary*: a sequence of |
| exit_codes.py | Person parsing | Exit code harmonisation for the 'Konec v legiích' field. |
| habsburg.py | Person parsing | Field-specific harmonisation for the VÚA *padlí v 1. sv. válce* dump |
| locations.py | Person parsing | Decomposes a free-text Czech place string into structured components. |
| occupations.py | Person parsing | The raw field is Czech free text: 119,046 non-empty values over 8,004 |
| provenance.py | Person parsing | Parses the free-text ``Zdroj informací`` field into two separate famil |
| ranks.py | Person parsing | Collapses the free-text rank fields onto **within-system ordinal** sca |
| religion.py | Person parsing | The raw ``Vyznání`` field is a small, abbreviated Czech vocabulary: de |
| schema.py | Person parsing | Single source of truth for column naming. The cleaning notebook |
| theater.py | Person parsing | Four-pass imputation of the Legion theatre from the raw csol.cz unit/p |
| units.py | Person parsing | Extracts a clean regiment label and unit type from |
| vua_courts.py | Person parsing | The Vojenský historický archiv publishes name-level inventories of thr |
| amateur_theatre.py | Geography & census | **Feasibility pilot.** Nothing here feeds ``data/final``. The module |
| amateur_theatre_model.py | Geography & census | The amateur-theatre model pass: the chronicle clauses the rules leave |
| census.py | Geography & census | Harmonises the Rumpler-Seger / MPIDR census workbooks in ``data/raw/ce |
| census_hu.py | Geography & census | Cisleithanian analogue: :mod:`utils.parsing.census` (okres-level Rumpl |
| census_labels.py | Geography & census | The Cisleithanian census (:mod:`utils.parsing.census`) carries **Germa |
| districts.py | Geography & census | Builds the **authoritative** 1910 district crosswalk for the Czech cro |
| districts_hu.py | Geography & census | The Cisleithanian analogue is :mod:`utils.parsing.districts` (Czech la |
| geonames.py | Geography & census | The RÚIAN tables (:mod:`utils.parsing.ruian`) stop at the official čás |
| lexikon.py | Geography & census | Parses the ČSÚ *Historický lexikon obcí* alphabetical register (the so |
| lexikon_sk.py | Geography & census | Transleithanian analogue of :mod:`utils.parsing.lexikon` (the Czech ČS |
| locative.py | Geography & census | Czech place names printed in the locative (*v Brně*, *ve Vodňanech*, * |
| matice.py | Geography & census | Matice česká and Matice moravská member registers — OCR to one row per |
| prng.py | Geography & census | 1910 Austrian Silesia did not end at today's Czech border: the eastern |
| records_geo.py | Geography & census | Maps a soldier's birth district string (``birth_okres``) to the 1910 p |
| records_geo_hu.py | Geography & census | Cisleithanian analogue: :mod:`utils.parsing.records_geo` (birth_okres |
| ruian.py | Geography & census | There are no 1910 municipal boundaries for Bohemia and Moravia, and th |
| spatial.py | Geography & census | Assigns a **court district (soudní okres / Gerichtsbezirk)** to any po |
| vanished.py | Geography & census | RÚIAN and Geonames list what exists today. A 1910 settlement that has |
| wikidata.py | Geography & census | Fetches Czech settlements (obec Q5153359 + část obce Q15978299) with * |
| linkage.py | Linkage | Detects individuals appearing in **both** final datasets so the |
| bakeoff.py | 1910 register | The bake-off, as a module: the configuration and the run loop, in one |
| build_contrast_sheet.py | 1910 register | The contrast sheet: the look-alike symbols the readers swap, side by s |
| build_de_page_map.py | 1910 register | Pair every Czech page of the 1910 register with the German pages that |
| build_glyph_bank.py | 1910 register | Cut the boxed glyphs out of the gold crops: the bank test A reads. |
| build_lexikon_figure.py | 1910 register | Cut the docs-site figure out of the 1910 register — images plus derive |
| build_mark_ledger.py | 1910 register | Fill the mark ledger (D-109) from what the r5 gold run left on disk, a |
| build_register_walkthrough.py | 1910 register | The docs site's Brzice walkthrough: from the printed line to the place |
| build_symbol_atlas.py | 1910 register | Cut the volume's own symbol legend into a labelled atlas of pictogram |
| build_symbol_atlas_de.py | 1910 register | Cut the German edition's own symbol legend into a labelled atlas (D-92 |
| build_token_review.py | 1910 register | Queue the suspect tokens of the gold sheets for a person to decide, on |
| check_transcription.py | 1910 register | Validate a human transcription worksheet, and turn it into a reader. |
| draw_gold_sample.py | 1910 register | Draw the pages a person will transcribe, reproducibly. |
| extract_prose_pages.py | 1910 register | The prose of the statutory towns' pages, from the text layer. |
| lexikon_1910.py | 1910 register | Podrobný seznam míst / Spezialortsrepertorium 1910 — grid, blocks, inv |
| lexikon_1910_attributes.py | 1910 register | What the register prints *beside* two glyphs: a school's classes and l |
| lexikon_1910_census.py | 1910 register | Reconcile transcribed 1910 register blocks against the okres-level cen |
| lexikon_1910_courts.py | 1910 register | The court each section of the register belongs to: one 1910 backbone c |
| lexikon_1910_crosswalk.py | 1910 register | Both volumes print the same 1910 municipalities of Bohemia, each under |
| lexikon_1910_de.py | 1910 register | Spezialortsrepertorium Böhmen 1910 — the German twin of the Czech volu |
| lexikon_1910_de_notes.py | 1910 register | The German edition's apparatus, read from the image alone (D-92). |
| lexikon_1910_de_table.py | 1910 register | The German volumes' table, cut into blocks from the image and read by |
| lexikon_1910_editions.py | 1910 register | The two editions of the 1910 register as two measurements of one truth |
| lexikon_1910_image.py | 1910 register | Page geometry of the 1910 register from the rendered page alone — no t |
| lexikon_1910_institutions.py | 1910 register | Decode the ``Úřady, ústavy`` footnote into an institution inventory. |
| lexikon_1910_ledger.py | 1910 register | The history of every mark of the 1910 register through the reading rou |
| lexikon_1910_names.py | 1910 register | Anchor 1910 register blocks to known municipalities, and let the volum |
| lexikon_1910_notes.py | 1910 register | The footnote apparatus printed below the 1910 register table. |
| lexikon_1910_pages.py | 1910 register | Read a whole page of the 1910 register with a cloud model, and price t |
| lexikon_1910_register.py | 1910 register | A frozen run (``output/frozen/<edition>_<pages>/``, notebook 15) settl |
| lexikon_1910_vision.py | 1910 register | Read the below-table apparatus from the *image*, and hand the result t |
| lexikon_1910_volume.py | 1910 register | One profile per volume of the 1910 register — what differs from book t |
| locate_glyphs.py | 1910 register | E3, first half: a box for every printed glyph of the apparatus (D-69, |
| make_gold_worksheet.py | 1910 register | Cut the gold-sample pages into a worksheet laid out the way the page r |
| make_gold_worksheet_de.py | 1910 register | Build the gold worksheets for the German edition (Spezialortsrepertori |
| read_transcription_md.py | 1910 register | Read the whole-page gold sheets, and grade the transcriber while doing |
| reader_batches.py | 1910 register | The readers, batched: queued requests in, cached replies out (D-108's |
| report_unlisted_abbreviations.py | 1910 register | The offices items no code in ``institution_abbreviations.csv`` reaches |
| rescore_passes.py | 1910 register | Re-score every cached symbol pass at the analysis unit. No model call, |
| run_de_table.py | 1910 register | The German volumes' table read by a model, block by block, as the Czec |
| run_e1_gemini_version.py | 1910 register | E1 — is the gap between the two cloud pipelines the model, or the view |
| run_e1b_second_reader.py | 1910 register | E1b — which cheap reader of another family errs somewhere else than Ge |
| run_e3_pages.py | 1910 register | E3 on any page: the locator and DINOv3 on the readers' saved section c |
| run_frozen.py | 1910 register | The frozen run: every stage of the reading route, for one edition and |
| run_glyph_e3.py | 1910 register | E3, second half: name every located glyph by its nearest neighbours on |
| run_glyph_test_a.py | 1910 register | Test A — does an image encoder tell the fill pairs apart at all? (SYMB |
| run_lexikon_1910_pilot.py | 1910 register | Calibration run for the 1910 special place register — four channels, o |
| run_r5_german_notes.py | 1910 register | The German notes arm of ``r5``: the German edition's apparatus, read b |
| run_table_arm.py | 1910 register | The table arm of ``r5`` — the seventeen printed columns read by a mode |
| run_verify_test_b.py | 1910 register | Test B — does one Opus verify call settle what Gemini and Haiku disput |
| score_certification.py | 1910 register | Score the sealed certification pages — once, against metrics written d |
| score_r5_german_notes.py | 1910 register | Score the German notes arm of ``r5`` against the German gold, and set |
| score_table_passes.py | 1910 register | Score the table readings against the gold tables, cell by cell. |
| section_images.py | 1910 register | The readers' section crops, saved as images for every page (D-112, ste |
| audit_full_run_pages.py | Reference tools | Every German full-run page, cut offline before anything is read: does |
| build_conflict_workbook.py | Reference tools | The Czech→HISCO dictionary flags keys ``review`` where the coding is n |
| build_curation.py | Reference tools | Encodes the accented-Czech names for the 1910 Czech-lands districts, k |
| build_czisco_bridge.py | Reference tools | CZ-ISCO (ČSÚ classification 80146, 2025) as a **Czech→English occupati |
| build_german_click_queue.py | Reference tools | A Token Review queue for clicking every pictogram of the German gold p |
| build_german_click_strips.py | Reference tools | Cut the German gold's office sections into strips of a few printed lin |
| build_hisco_authority.py | Reference tools | One row per **(hisco, status, relation, product)**, carrying the Engli |
| build_matice_read_sheets.py | Reference tools | Sheets of scanned lines for re-reading the Matice residences the OCR g |
| build_occupation_curation.py | Reference tools | Emits the **skeleton** of the Czech occupational dictionary: every nor |
| build_page_index.py | Reference tools | Build the page index for Österreichische Statistik XCIII/1 (Unterricht |
| build_sk_curation.py | Reference tools | Magyar → Slovak/Rusyn name curation for the 1910 Upper-Hungary (Felvid |
| build_theatre_gold_page.py | Reference tools | The ruling page for the theatre model pass's gold (D-157). |
| build_unit_authority.py | Reference tools | Derives a **factual authority table of Czechoslovak Legion units** fro |
| build_unit_timeline.py | Reference tools | Where every Czechoslovak Legion unit was, whom it answered to and who |
| check_atlas_parties.py | Reference tools | ``atlas_iv37_parties.csv`` is a **hand transcription of a photograph** |
| check_unit_timeline.py | Reference tools | ``unit_timeline.csv`` is parsed by ``build_unit_timeline.py`` from the |
| check_unterricht_transcription.py | Reference tools | Invariants on the transcription of Österreichische Statistik XCIII/1 ( |
| czech_glosses.py | Reference tools | The authored half of the occupation pipeline, and the piece no externa |
| download_hisco_sources.py | Reference tools | The occupation pipeline needs two external ingredients, and only one o |
| draw_education_sample.py | Reference tools | Draw the `Vzdělání` strings a person will code by hand, reproducibly. |
| german_candidate_diagnosis.py | Reference tools | Which candidate-step filter removes the German glyphs the author click |
| german_click_recall.py | Reference tools | How well does the locator's box land on the glyph the author clicked? |
| german_locator_trial.py | Reference tools | Does a locator trained on the German gold place German claims better t |
| german_supervised_placement.py | Reference tools | Does a glyph classifier trained on the author's clicks place German cl |
| map_intensities.py | Reference tools | The intensities of ``run_intensities.py`` side by side, one figure per |
| map_matice.py | Reference tools | Maps of the Matice measure at three levels: political okres, court dis |
| merge_matice_readings.py | Reference tools | Merge the residence readings made from ``build_matice_read_sheets.py`` |
| prep_slices.py | Reference tools | Prepare reading slices for the qualitative pass (scratch; not part of |
| run_amateur_theatre_pilot.py | Reference tools | Feasibility pilot — the amateur-theatre database as a pre-1914 covaria |
| run_census_berufsstellung_check.py | Reference tools | is a `rolník` a farm *owner* or a farm *worker*? |
| run_czisco_check.py | Reference tools | Matches each curated Czech key against the **head noun** of a CZ-ISCO |
| run_german_detect_by_naming.py | Reference tools | Detect by naming: let the bank find the German glyphs, instead of a lo |
| run_german_oracle.py | Reference tools | The oracle test: does the Czech-built bank name German glyphs when the |
| run_german_reader_boxes.py | Reference tools | The reader gives the box: can DINOv3 check German claims on the boxes |
| run_intensities.py | Reference tools | Four intensities side by side, per 1,000, at three levels: political o |
| run_link_bridge.py | Reference tools | Applies ``czech_glosses.py`` to the curation skeleton and fills in the |
| run_matice_pilot.py | Reference tools | Pilot — the Matice česká and Matice moravská member registers, read in |
| run_mosaic_beruf_check.py | Reference tools | does the German word for a farmer mean an *owner*, one record at a tim |
| run_occcanine_check.py | Reference tools | Codes the **German glosses** with OccCANINE (Dahl, Johansen & Vedel, " |
| run_unterricht_check.py | Reference tools | Is the education ladder the one the Austrian state actually operated? |
| run_vua_courts_pilot.py | Reference tools | Pilot — the VÚA military-court inventories as a Habsburg-side record o |
| score_education_gold.py | Reference tools | Score a hand-coded education worksheet against `education.py`. |
| score_sealed_abbreviations.py | Reference tools | Score the text abbreviations on the sealed certification pages — a sec |
| statutory_institutions.py | Reference tools | The statutory cities' institutions, from their verified prose: stage 2 |
| statutory_prose.py | Reference tools | The statutory cities' prose, transcribed precisely: two vision readers |
| verify_locators.py | Reference tools | Check every ledger entry's locator against its slice (scratch tool for |
| build_docs.py | Output & support | Renders every authored reference document, the tabular crosswalks, and |
| build_fonts.py | Output & support | Subset the Libertinus faces the docs site embeds -> utils/docs/fonts/* |
| build_validation_workbook.py | Output & support | Draws a **tier-stratified** sample of resolved records and writes an E |
| check_ostafin_silesia.py | Output & support | Austrian Silesia 1910 against Ostafin et al. (2020): an independent tr |
| data_checks.py | Output & support | One definition of "is the built data sound?", used in two places: |
| latex_tables.py | Output & support | LaTeX table generation utilities. |
| lexikon_population.py | Output & support | The 1910 population of the ČSÚ *Historický lexikon obcí ČR 1869–2005*, |
| run_completeness.py | Output & support | Whether a frozen run has every reply it asked for, before anything is |
| score_sealed_retest.py | Output & support | The full run's readings of the sealed certification pages, scored — a |
| th_style_v113.py | Output & support | A personal, reusable Matplotlib configuration module built around the |
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 projectou1wk); a feasibility pilot so far, run from notebook 01 intodata/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/) andrun_oeulk_downloads.py(Österreich-Ungarns letzter Krieg, all sixteen parts →library/ah-army/).progress.pyis their shared progress bar.fetch_vua_courts.py— the VÚA inventories of three Habsburg military courts, three PDFs, fetched once intodata/raw/vua_courts/(parsed byutils/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, whichFalsehad 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 dominantpěší),ah_unit_org(Common army / Landwehr / Honvéd / Landsturm),ah_regiment_no,ah_n_units. - Note enrichment
Poznámka→note_legion(+ matched phrasenote_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):
add_birth_okres_id— resolve the raw district string against 1910 political-okres names and court-district names (both → parentokres_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). Thesettlements-less call fromlexikon.pykeeps first-match, which is correct there because the register writes the 1910 period name for the Bohemian twin.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}_cisleithanianis 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.add_birth_settlement_okres— second pass via the municipality: if the raw okres exists, accept a settlement whose okres timeline contains it (tiersettlement— 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; tiermuni, the weakest).add_landagain (pass-2 resolved more), thenadd_settlement_attrs— standardised Czech/German municipality names,court_id/cs/de,lat/lon/qidfrom the matched settlement (keyed(name, okres_id), unique-name fallback land-vetoed), andadd_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 onsettlements_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 withtheater.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 underdata/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 (joinbirth_okres_id = okres_id); kept separate so the canonical files stay lean.*_census_hu_1910.parquet— the Transleithanian analogue (joinbirth_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) andlexikon_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.pyputs the Matice, theatre, legionnaire and desertion intensities side by side at okres, court and settlement level (data/interim/intensity/,INTENSITY_MAPS.md);map_intensities.pyandmap_matice.pydraw them. - Matice:
run_matice_pilot.pyreads the member lists intodata/interim/matice/and sums them per okres;build_matice_read_sheets.pycuts the lines whose residence the OCR garbled into sheets for reading by eye, andmerge_matice_readings.pymerges the readings intoreference/places/matice_residence_readings.csv, never overwriting one made by eye. - Amateur theatre:
run_amateur_theatre_pilot.pyparses the scraped database and builds the measure indata/interim/theatre/;build_theatre_gold_page.pywrites the ruling page for the model pass's gold (D-157). - The 1910 register, from outside the freeze:
audit_full_run_pages.pycuts every German full-run page offline before anything is read;statutory_prose.pyandstatutory_institutions.pytranscribe the eleven statutory cities' prose and turn it into one row per institution (D-178);score_sealed_abbreviations.pyscores the text abbreviations on the sealed pages. - German E3 trials (D-175, all negative on placement):
build_german_click_queue.pyandbuild_german_click_strips.pyset up the author's clicks on the German gold;german_click_recall.pymeasures 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.pyandrun_german_reader_boxes.pyare the attempts. - Units:
build_unit_timeline.pyreads where every Legion unit was, month by month, from Fidler (2025) intounit_timeline.csv, held bycheck_unit_timeline.py;run_vua_courts_pilot.py(§2). - Occupations:
run_mosaic_beruf_check.py, the Vienna Database check onrolník(D-34). - Background reading:
prep_slices.pyandverify_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) intodata/raw/hisco/and writesMANIFEST.TXTwith 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 fromdata/raw/legionnaires/(notdata/final/, which would be circular). Merges onkeyand never overwrites a hand-entered code.run_link_bridge.py— appliesczech_glosses.py: German gloss → exact match in the HISCO project's German labels → code; ambiguous glosses are flagged, not assigned;HISCO_OVERRIDEbeats the bridge, andADJUDICATEDruns last and ownsreview(D-41), writing each key's verdict to the curation'sverdictcolumn.run_occcanine_check.py— validation only, likebuild_unit_authority.py. Codes the same German glosses with OccCANINE as an independent second coder. torch is deliberately not inrequirements.txt.build_czisco_bridge.py/run_czisco_check.py— validation only. CZ-ISCO as a third coder on the Czech reading itself; never writeshisco(D-28).run_census_berufsstellung_check.py— validation only, writes no file at all. Testsrolní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 intocensus_okres_1910.parquetand previously unused. Two tests: share-of-births withnádeník/dělníkplacebos, 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— emitsunterricht_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 ascheck_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 inKNOWN_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.pytests 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, thepok.decision, Table 34 againsteducation_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. SeeVALIDATION_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
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.
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.
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:
- Read the district straight off the address. The record often names the district — just look it up.
- 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.
- 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:
lexikon._CURATED_OKRESfills the okres on the twelve headwords the register leaves without one — for a statutory city, the city district and not the rural ring (okresBrno302-5, notBrno-venkov302-4). Těšín, which a modern Czech register has no reason to carry at all, is appended outright.records_geo._NOT_A_PLACEstops 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:
- A bare district name means the rural district, for all ten pairs.
- A trailing rural qualifier is stripped by rule, not by luck.
Olomouc venkov,Brno (venkov),Brünn Umgebungused to reach the right unit through the fuzzy tier, which happened to drop the extra token — and were afterwards indistinguishable from a bareOlomouc. They now resolve at theokrestier. - 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
undecidedinbirth_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).
- A broken year or
okr.hid the 1910 okres. The text layer splitsv r. 186 9-1910andv 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_ocrrejoins a year only where the register prints one (afterr.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. - The register's bare
okr. Berounis Moravian. In 1910 there was no Bohemian okres Beroun — the register files the town itselfv okr. Hořovice— and all 43 rows printing bare Beroun carryMoravský Berounlater 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_1910reads them as Moravský Beroun, only where the row's timeline says so. - A shared key took whichever row came last.
add_settlement_attrskeys 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:
okres_1910— candidates whose point lies in the settlement's 1910 okres;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);modern_okres— candidates in the modern okres the register's ownokres_alltimeline 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:
- fuzzy (ratio ≥ 90, as in
records_geo) in the 1910 okres — 21 found, 9 kept; - 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; - 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):
- 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. - 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 thefuzzytier and were afterwards indistinguishable from a bareKošice. They now resolve atjaras, and the same regex serves both countries (records_geo._RURAL_SUFFIX). - 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
undecidedinbirth_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):
(accented Czech district names),
district_cs_curation.csv (Magyar→Slovak district names + the
Wikidata-validated exonyms).district_sk_curation.csv
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
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.
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.
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.
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.
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
| place | printed name · source | settlement_id | in the authority | status | score / runner-up |
|---|---|---|---|---|---|
| municipality | Brzice, Brzitz table | brzice:1 | Brzice | matched | 100 / 72 |
| osada 1 | Brzice, Brzitz table | brzice:1 | Brzice | matched | 100 / 72 |
| osada 2 | Grund table | zlibecek:1 | Žlíbeček (1910: Grund) osada of Brzice | matched | 100 / 60 |
| osada 3 | Harcov, Harcow table | harcov:2 | Harcov osada of Brzice | matched | 100 / 66 |
| osada 4 | Žďár, Žďar table | zdar:9 | Žďár osada of Brzice | matched | 100 / 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
| place | mark | count | rule | settled by | common category |
|---|---|---|---|---|---|
| osada 1 | [cooperative] | 1 | agreed | agreed | [cooperative] |
| osada 1 | [fire-brigade] | 1 | agreed | agreed | [fire-brigade] |
| osada 1 | [midwife] | 1 | agreed | agreed | [midwife] |
| osada 1 | [school] | 1 | agreed | agreed | [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
| column | value |
|---|---|
settlement_id | brzice:1 |
edition | cz |
editions_seen | cz |
run | cz_gold |
court_id | 301057121 |
court_cs | Česká Skalice |
okres_id | 301057 |
muni_seq | 1 |
printed_name | Brzice, Brzitz |
anchor_status | matched |
anchor_score | 100 |
n_parts | 4 |
n_parts_anchored | 4 |
abbreviations_read | False |
n_claims | 4 |
n_claims_disputed | 0 |
| column | value |
|---|---|
n_claims_author | 0 |
n_claims_opus | 0 |
n_claims_reader_of_record | 0 |
rc_cure_seat | False |
rc_parish_strict | False |
n_rc_cure_seat | 0 |
n_coop | <NA> |
n_cat_cooperative | 1 |
n_cat_fire_brigade | 1 |
n_cat_midwife | 1 |
n_cat_school | 1 |
n_mark_cooperative | 1 |
n_mark_fire_brigade | 1 |
n_mark_midwife | 1 |
n_mark_school | 1 |
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 cell | German name, folded | German No. | German page prints | paired on the gold by |
|---|---|---|---|---|---|
| later page | Újezdec, Aujezdetz | — | 1 | Aujezdetz, Oujezdec | refused — no candidate on the gold page |
| 3 | Česká Skalice, Böhmisch Skalitz | bohmischskalitz | 2 | Böhmisch Skalitz, Česká Skalice | name |
| 1 | Brzice, Brzitz | brzitz | 3 | Brzitz, Brzice | name |
| 2 | Červená Hora, také Červená Hůra, Čerwena Hora | cerwenahora | 4 | Cerwena Hora, Červená Hora | name |
| 4 | Doubravice, Doubrawitz | doubrawitz | beyond the figure | ||
| 5 | Hořičky, Hořička | horicka | beyond 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.
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][gauging][customs][brickworks][hamlet][ev-parish-ab][ev-parish-hb][ev-filial-ab][ev-filial-hb][ev-preacher-ab][ev-preacher-hb][cadastre-office][fire-brigade][transport-station][inn][castle][gamekeeper][single-house][cadastral][monastery][colony][farrier][baths][doctor][pharmacy][sanatorium][kindergarten][besides][mill][huntsman][obec][mesto][mestys][hospital][port][school][revenue-guard][creche][osada][knacker][manor-farm][midwife][pilgrimage][post-agency][post][suburb][scattered][shepherd-hut][solitary][cottage-group][house-group][savings-bank][revenue-admin][cask-gauging][canteen][revenue-technical][telephone][telegraph][factory][tourist-hut][ves][poorhouse][cooperative][vet][ruin][gendarme][city-part][mestys-part][rc-parish][rc-parish-exp][rc-vicariate][rc-filial][rc-chapel][rc-curate][rc-pilgrim-chapel][rc-pilgrim-parish][rc-pilgrim-church][school-exp][manor-house][rail-station][rail-stop][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.
| Check | Passed | Failed | Unreadable | Not applicable | Pass rate |
|---|---|---|---|---|---|
| I1_sex | 632 | 7 | 13 | 6 | 96.9% |
| I2_confession | 543 | 49 | 57 | 9 | 83.7% |
| I3_language | 562 | 37 | 49 | 10 | 86.7% |
| I5_sequence | 563 | 18 | 38 | 39 | 91.0% |
| I8_parts | 241 | 70 | 0 | 2 | 77.5% |
| I4_block (all columns) | 803 | 69 | 69 | 2,599 | 85.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.
- The rules are in the ALTO.
GraphicalElemententries 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. - The area column is printed on municipality rows only. That is the block
delimiter. Typography cannot be used for it: the NDK
TextStyleentries are synthetic — Times New Roman, sizes 5–10 — and carry no weight. - 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
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.
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.
| observed | rate | 95 % upper bound (exact) | |
|---|---|---|---|
| false claims, of the route's claims | 1 of 503 | 0.20 % | 1.10 % |
| missed claims, of the printed claims | 3 of 505 | 0.59 % | 1.73 % |
| any error, per printed claim | 4 of 505 | 0.79 % | 2.02 % |
| instances of the symbol on the sealed pages | symbols | claims | missed | lowest 95 % lower bound on sensitivity |
|---|---|---|---|---|
| ten or more | 13 | 416 | 1 | 0.62 |
| three to nine | 14 | 74 | 2 | 0.09 |
| one or two | 12 | 15 | 0 | 0.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.
| observed | rate | 95 % upper bound (exact) | |
|---|---|---|---|
| false claims, of the route's claims | 31 of 629 | 4.93 % | 6.92 % |
| missed claims, of the printed claims | 23 of 621 | 3.70 % | 5.51 % |
| any error, per printed claim | 54 of 621 | 8.70 % | 11.19 % |
| instances of the symbol on the sealed pages | symbols | claims | missed | lowest 95 % lower bound on sensitivity |
|---|---|---|---|---|
| ten or more | 14 | 528 | 17 | 0.18 |
| three to nine | 12 | 74 | 2 | 0.29 |
| one or two | 15 | 19 | 4 | 0.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.
| observed | rate | 95 % upper bound (exact) | |
|---|---|---|---|
| false claims, of the route's claims | 1 of 619 | 0.16 % | 0.90 % |
| missed claims, of the printed claims | 5 of 623 | 0.80 % | 1.86 % |
| any error, per printed claim | 6 of 623 | 0.96 % | 2.08 % |
| instances of the symbol on the sealed pages | symbols | claims | missed | lowest 95 % lower bound on sensitivity |
|---|---|---|---|---|
| ten or more | 14 | 530 | 2 | 0.66 |
| three to nine | 12 | 74 | 2 | 0.29 |
| one or two | 15 | 19 | 1 | 0.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.
- 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_frozen0.21.0,STATUTORY_TABLE_PAGESfor 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_register1.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, statusauthor, 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 flaggedis_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. - 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_image0.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_templatein 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
r2bake-off, old decoder and gold) and none has been read underr4; 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_referencereports 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.
Revision, 2026-09-22 — the reader is no longer the weakest link
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.
- 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 usingentry_exactas the headline. Then re-score every cached pass — no spend. - 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.
- 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 onatlas_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). - 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.
- 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.pngtruncates 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_exactby 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_entriessplits on_PART_CONT_REinto 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 inseq_repaired, never renumbered.build_inventoryemits 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_symbolsgives 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 (
StagainstB).
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_exactbeside precision and recall from here on; an F1 that improves whileentry_exactdoes 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_levelas 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
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.lexikon_1910_symbols.py
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 —
baseandagainare 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
. Re-running the script
reproduces the draw exactly.places/gold_sample_pages.csv
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, ústavysection, replace it with the next free scan forward and record it in thereplacedcolumn — 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
Mechaniceagainst the table'sNechanicestranded 46 keys), and_repair_runlet 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.csvholds 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
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
- Notebook 02, §3c writes
data/final/geo/settlements_1910(the place authority). - Notebook 15, §18 (
PAGES = "full") anchors the register to it and writesdata/final/register/. - Notebook 02, §3e builds the map crosswalk
data/final/geo/settlements_1910_geo, which breaks ties by the register's court (ruian.register_courtsreadsregister_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
- The run settles the claim. A claim is one institution printed in one part:
(page, section, municipality number, part number, mark). Pictograms come fromfinal_inventory.csv(one row per claim, with the printed count and the rule that settled it); text abbreviations and cooperative letters come fromfinal_abbreviations.csv, where the run wrote one (D-134). Each section's 1910 court comes fromcourts.csv(D-122). - The place gets its key. A place is keyed by its section's court, not by the printed label,
which the text layer damages (
mechanicefor Nechanice; D-121, D-127). A section with no court is keyedunresolved:<label>and pairs only with itself. - 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).
- 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Úvizaliases insettlement_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 sharedsettlement_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'ssettlement_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. - 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 istxt:plus its English (txt:district court), so the Czecho. s.and the GermanBG.meet while a printed word never merges with a pictogram. - 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_totalagainst 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_seatorrc_parish_strict, nevern_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 frequentdin 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 treatn_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 writtenfinal_abbreviations.csvyet, 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_partitionedin 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_idstability (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 ofsettlements_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
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:
- When it was active: the year it started, the decades it played (the 1880s, 1890s, 1900s), and sometimes the year it ended.
- What it did: a short chronicle of dated notes (1887 founded, 1901 gave 12 performances, 1905 gave to the National Theatre fund).
- 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
- 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). - 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. - Codes.
code_clauses()applies the author's codebook (D-141): which event types put Czech theatre in a place and year, and how. - Evidence.
evidence_table()puts every dated record into one interval form, keyedevidence_id, and gives each company onecompany_idacross sources. - Panel.
place_year_panel()turns the evidence into one row per place and year, every measure certain and possible. - 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.
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.
Open
- The model pass is built, not run. Notebook 16, with
SPEND = Trueon 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 (scopeall), whethersource_yearshould count as an end, and the bar.companies_performingwaits on it: the readings carryperformer_kindandperformer_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_possiblecounts a founding as possible in every year after it. Closing open ends (D-145) is why the full build'sfounded_possibleis 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_theatre0.6.1): the saved settlement table is keyed bysettlement_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
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
| list | part | printed | found | |
|---|---|---|---|---|
| mc1873 | founder | 2,633 | 2,614 | 99.3 % |
| mc1873 | founder corporate | 146 | 155 | 106.2 % |
| mc1873 | founder deceased | 815 | 797 | 97.8 % |
| mc1890 | founder | 2,658 | 2,650 | 99.7 % |
| mc1890 | founder corporate | 267 | 255 | 95.5 % |
| mc1890 | founder deceased | 1,617 | 1,599 | 98.9 % |
| mm1891 | founding | 18 | 17 | 94.4 % |
| mm1891 | active | 146 | 137 | 93.8 % |
| mm1891 | contributing | 641 | 595 | 92.8 % |
| mm1909 | honorary | 3 | 3 | 100.0 % |
| mm1909 | founding | 10 | 10 | 100.0 % |
| mm1909 | active | 103 | 101 | 98.1 % |
| mm1909 | contributing | 757 | 735 | 97.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
| matched | prague | outside | ambiguous | unmatched | none | land 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):
where is unit 's shrunk member rate per 1,000 inhabitants in its own land's society,
so is members per 100,000, and , are the mean and standard
deviation over the society's territory (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
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 okresy | Czech share >= 0.5 | Böhmen, Czech >= 0.5 | Mä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
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.
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
hiscoor 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řízenecorpří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 censusday_laborerscolumn, not here.
5. Where the codes come from
Three sources, in this order of authority:
- German link bridge. Each Czech key gets a German gloss (
obuvník→Schuhmacher). The gloss is matched exactly against the 1,199 German labels inhisco_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. - English label match against the authority's own
label_en, exact only. - Hand assignment (
HISCO_OVERRIDE) for the 434 keys the German list does not reach, and for every judgement call. Each code was read offhisco_authority.csv— none was recalled from memory — and each carries a one-linenote.
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íkin 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ínandnádeníkcarry the labourer codes separately.domkář(cottager) andmalorolníkare 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)returnsNoneand 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 intoexit_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 (
occupations1.3.0, D-42).-1is "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_enandhisco_majorare 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.pyis 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.pycarries the columns; notebook 02 was last re-run 2026-09-23 anddata/finalis 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.csvmaps 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:
- 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. 8andob. 5 mě. 3are equal in years and unequal in curriculum; they must not be coded as if the second were a full rung above the first. - 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.
- 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) orob. 5 reálka 7(81) is a boy who left the compulsory track at ten;ob. 5 mě. 3is one who did not. This is a genuine ordinal break, and the one the field measures most reliably. - 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 asgym.
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. 8did not receive the same schooling, and the difference runs along the urban/rural axis we already carry at okres level. An argument for usingbirth_okrescovariates 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:
- 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.
- 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.andodb.do. - 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.orobch.rather thanpok.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 ofob./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:
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 beforemě.andob., everyob. 5 mě. 3 pok. 3record — the classic apprentice itinerary — currently lands in the same bucket asgym.8,805 records, 8.3% of the non-empty field. This is the largest single error in the variable, and it confirms ROADMAP B3.- Dot-less and long variants fall through.
pokr.,pokrač.,3mě,2mědo not contain the literal tokenspok./mě.;hosp.,odb.,kadetka,konzervatořhave no rule at all, and the 1.1% unmatched tail returnsNoneunless anob.elsewhere in the string rescues it at Primary. uč.ústavis coded level withgym./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.- 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. 3andodb. 2remain years of a thing whose size we do not know,education_completestays<NA>across that branch, and noeducation_years_totalis 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 waybirth_okresandbirth_jaras_idare 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 inlibrary/— 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 thepok.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); inlibrary/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 scorededucation_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. Ncounts years or classes. Newly opened, and it breaks a column. §2a's Table 34 shows the eight-class Volksschuleeducation_completescores against existed 10 times in Cisleithania and twice in the Czech lands. Among 11,986 Czech-lands-born records witheducation_top == "elementary"and a stated year, 3,894 are scored complete against that form and 3,137 readingob. 5— the modal full Bohemian Volksschule — are scored incomplete. Logged as D-53 with two candidate resolutions: (a) seteducation_completeto<NA>for a bareob. 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). Somě.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 adjudicatedrolní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:
- A generic word yields to a specific one.
odb.(odborná = "specialised") andstř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). - 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álkadoes 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_completeispd.NAfor 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 onlypok.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. 5alone is therefore incomplete;ob. 5 mě. 3is complete. prům.vsvyšší prům.splits on the qualifier, one rung apart, the same wayoccupation_qualifiersplitszámečníkfromstrojní 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.
output/figures/descriptives/composition/educ_by_theatre.pngRank laddersHow it works
The two rank systems and the reset. · source: reference/ranks/rank_ladders.md
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.
ordinalcolumns 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 thej.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. Likewiserotmistris not a k.u.k. rank here (the cavalry captain is rytmistr = Rittmeister); treat Habsburgrotmistras either an OCR/spelling slip for rytmistr or a Legion-side rank (see §B). The same treatment applies topodpraporčí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 booleanone_year_volunteerflag 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.pyv1.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). Habsburghabsburg_rank→habsburg_rank_{clean,en,ord,ord_low,jd,branch,tier,common,band}(98.8 % coverage); Legionlegion_rank(enrolment) andlast_legion_rank→ the same nine-column families (99.6 % / 99.5 % coverage). All three wired intonotebooks/02_data_cleaning§1 and registered inutils/parsing/schema.py. Compounds keep both ends (ord= highest reached,ord_low= lowest listed);j.d.is a flag. - Habsburg drops
podporučík,rotmistrandpodpraporčíkto<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) androtmistr(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ánis collapsed to the kapitán rung (11),podkapitánto the nadporučík rung (10), andštábní rotmistrties withrotmistrat 6 (the §B "6⁺" is not separately encoded) for the integer scale. - ✅ Cross-system bridge built (v1.3.0, 2026-07-09).
add_rank_columnsalso emits the system-blind{prefix}_common(ALT 1, §G —RANK_COMMON) and the coarse{prefix}_band(raw APersP-01 OF/OR);add_rank_resetdifferences the common columns intorank_reset(+rank_reset_bandrobustness). The two within-system ordinals remain not cross-subtractable (intro, §F) — the reset rides on_commononly. A machine-readablereference/ranks/rank_ladders.csvremains optional (the module embeds the maps, mirroringeducation.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_BANDencodes — š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 asrank_bandrobustness. Officer-candidates (praporčík etc.) are OR‑5–9 (warrant band), not OF. - ⚠️ Still open (Legion-side, minor): střelmistr exact level and
občanhandling. - 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_2003is unaffected either way).
Rank recognition 1918–38How it works
Legal memo behind the common scale. · source: reference/ranks/rank_recognition_1918_1938.md
Research memo — Sbírka zákonů a nařízení (ASPI opispdf scans). Compiled 2026-06-28.
Explain it simply
When the war ended, hundreds of thousands of men who had worn one uniform had to be fitted into another — and the new Czechoslovak state had to decide what their old rank was worth. This sheet is the answer, dug out of the statute books.
The law was openly lopsided, and knowing that is the point. Legion service was a reward: preferential appointments, generous crediting of time served, seniority back-dated as far as 1914. Former Austro-Hungarian officers were let in grudgingly and often a grade down. Two men of identical ability ended the decade in different places purely because of which army they had been in.
It sits in the documentation because it explains a rumour the data would otherwise seem to support. Legionnaires believed officers were being demoted; the statutes show the opposite policy. So when the data shows a Habsburg officer entering the Legion at a low grade, the explanation is an entry procedure, not a punishment — and that distinction changes what the desertion result means.
Question. How was a soldier's earlier military rank — from (1) the Czechoslovak Legions, (2) the former Austro-Hungarian / Habsburg army, or (3) other pre-Czechoslovak service — legally recognised, converted, ordered by seniority, or used to determine his rank in the Czechoslovak army after 1918?
Method. Year-by-year sweep of the ASPI yearly index https://www.aspi.cz/opispdf/opispdf/[YEAR].html
for 1918–1938, filtering titles for rank/army/legion/service-crediting terms. Core statutes were
read verbatim by rasterising the scanned částka PDFs and transcribing the relevant paragraphs
(the PDFs carry no text layer). Verbatim quotes are marked [OCR-verbatim]; everything else is
a close paraphrase from the index title + statute context and is flagged for verification.
Bottom line. The post-1918 framework was effectively settled in 1919–1921 and only amended thereafter (1927 service-category decrees; 1934 consolidation). The legal logic is asymmetric: legionary service was a privilege to be rewarded (preferential posts, generous service-time crediting, seniority back-dated to 1914), whereas former Austro-Hungarian officers were admitted only at the discretion of the Ministry of National Defence and otherwise pensioned off — with a punitive track for those judged disloyal. Note the scope distinction below before using any of this for the thesis's rank-reset variable.
1. Structured findings table
Categories (col. F) use these tags: H = rank hierarchy/ordering · C = conversion of a prior rank into a ČS rank · O = recognition of officer status · S = seniority / order of precedence · A = appointment / promotion rule · T = crediting of previous service time.
| # | Year | Legal source (no., type, title) | Relevant passage / paraphrase | What it does (rank logic) | Applies to | Categories | Source URL | Confidence |
|---|---|---|---|---|---|---|---|---|
| 1 | 1919 | 282/1919 Sb., zákon ze dne 23. 5. 1919, o úlevách při přijímání legionářů do státních úřadů | [OCR-verbatim] "Vláda se zmocňuje, aby stanovila zásady, dle nichž příslušníkům československých zahraničních vojsk (legionářům) mohou býti [poskytnuty] slevy [a] dispense od všeobecných i zvláštních formálních podmínek pro dosažení různých státních služeb … (ku př. co do … léta, věku, stavu, formálního průkazu o předchozích službách atd.)." | Empowers the government to waive formal entry conditions (age, education, proof of prior service) for legionnaires entering state office — a concession framework, not a rank rule. Foundation for the rank/seniority bonuses that follow. | Legionnaires (entry to state service generally) | T, A | https://www.aspi.cz/opispdf/1919/060-1919.pdf | High (verbatim) |
| 2 | 1919 | 457/1919 Sb., zákon, o připočtení vojenských let státním zaměstnancům | Paraphrase: war/military years served are credited toward service seniority of state employees. | Crediting of previous service time (civil-service seniority), the civilian analogue to §6 of 195/1920. | State employees incl. ex-soldiers | T | https://www.aspi.cz/opispdf/1919/093-1919.pdf | Medium (title + context; not OCR'd) |
| 3 | 1919 | 462/1919 Sb., zákon ze dne 24. 7. 1919, o propůjčování míst legionářům | [OCR-verbatim] §1: "Legionáři, t.j. vojíni československých [vojsk], kteří vyhovují podmínkám stanoveným v § 2, mají na místa, jichž propůjčení státem [přísluší], přednostní právo a zaručení." §2–3 define a legionnaire as a Czechoslovak volunteer in the Russian, French or Italian legions. The reserved quota is ≥ 50 % of the listed posts. | Preferential right + guarantee of state posts for legionnaires; explicit statutory definition of "legionnaire." Concerns access to posts, not military rank ranking, but cements the privileged legal status. Implemented by nař. 151/1920 Sb. | Legionnaires (Russian/French/Italian) | A, (T) | https://www.aspi.cz/opispdf/1919/093-1919.pdf | High (verbatim) |
| 4 | 1920 | 193/1920 Sb., Branný zákon republiky Československé (19. 3. 1920) | Paraphrase: constitutes the unified branná moc; §6 "Vstup do československých služeb vojenských …"; framework under which ranks, conscription and the standing army are organised. §3 governs the return of the Russian legions (date set by nař. 8/1921). | Establishes the army into which both groups were incorporated; the enabling frame for 194 & 195 (its sister laws in the same částka 41). | All | (H), A | https://www.aspi.cz/opispdf/1920/041-1920.pdf | Medium-high (read; specific § paraphrased) |
| 5 | 1920 | 194/1920 Sb., zákon ze dne 19. 3. 1920, 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é a jímž se upravují zaopatřovací nároky bývalých vojenských gážistů, kteří se na československém národě provinili | [OCR-verbatim] §1: "Ministerstvu národní obrany přísluší právo přijímati podle vlastního uvážení bývalé rakousko-uherské, rakouské a uherské vojenské gážisty do vojenských služeb republiky Československé …" Later §§: those not taken over keep/convert to zaopatřovací (pension) nároky; a separate track strips/limits provision for those who "se na československém národě provinili." | Discretionary admission of former Habsburg officers (no automatic recognition or conversion of rank); welfare/severance for the non-admitted; punitive treatment of the disloyal. The decisive contrast with the legionnaire regime. | Former Austro-Hungarian / Austrian / Hungarian officers & military officials (gážisté) | C, O, A, T | https://www.aspi.cz/opispdf/1920/041-1920.pdf | High (verbatim §1; later §§ closely paraphrased) |
| 6 | 1920 | 195/1920 Sb., zákon ze dne 19. 3. 1920, o úpravě služebních počitků československého vojska | [OCR-verbatim] Díl I heading: "Důstojníci a vojenští úředníci (gážisté zařadění do hodnostních tříd), pak důstojníci a úředníci čekatelé." §1 sets a pay scale by hodnostní třída II–XI. §6 "Započítatelná léta jsou: A. u domácího vojska … b) Válečná léta. Za každý kalendářní rok 1915 až 1919, v němž byl gážista aspoň po jistou dobu příslušníkem československého vojska na Rusi, v Italii neb ve Francii (čs. legionář) …"; B. for legionnaire officers: "Léta strávená v legiích republiky se počítají [za dobu] od 24. července 1914 …" (also crediting war years in the Habsburg army or in captivity). §7 totals the service time governing advancement (postup). Díl II covers NCOs/gážisté outside the rank classes (pay classes 1–12). | The operative seniority/ordering law. Defines the rank-class hierarchy (II–XI) and converts prior service into countable years for pay and promotion. Legionary service is credited from 24 July 1914 — the most favourable back-dating, giving legionnaires a seniority edge; Habsburg war years and captivity also count. | ČS army officers, military officials, NCOs — incl. legionnaire and ex-Habsburg officers once admitted | H, S, T, A | https://www.aspi.cz/opispdf/1920/041-1920.pdf | High (verbatim key passages) |
| 7 | 1920 | 222/1920 Sb., zákon, o započtení služební doby státním zaměstnancům | Paraphrase: general crediting of prior service time for state employees (civilian counterpart, harmonised with 457/1919). | Service-time crediting (civil service). | State employees | T | https://www.aspi.cz/opispdf/1920/045-1920.pdf | Medium (title + context) |
| 8 | 1921 | 269/1921 Sb., nařízení, jímž se provádí branný zákon republiky Československé | Paraphrase: detailed implementing decree of the 1920 Branný zákon (≈7 MB); fixes the operational rules on ranks, enrolment and standing. | Implements the rank/enrolment frame. | All | H, A | https://www.aspi.cz/opispdf/1921/066-1921.pdf | Medium (not OCR'd) |
| 9 | 1921 | 440/1921 Sb., nařízení, jímž se provádějí některá ustanovení zákona č. 195/1920 o úpravě služebních počitků čs. vojska | Paraphrase: implementing decree to 195/1920 — operational detail of the service-year counting and pay-class placement. | Implements seniority/service-time crediting. | ČS army gážisté | S, T | https://www.aspi.cz/opispdf/1921/116-1921.pdf | Medium (not OCR'd) |
| 10 | 1921 | 8/1921 Sb., nařízení, kterým se stanoví den návratu ruských legií podle § 3 zákona 193/1920 | Paraphrase: fixes the legal return date of the Russian (Siberian) legions — the moment from which their post-1918 status is reckoned. | Timing/seniority reference for the largest legion cohort. | Russian/Siberian legionnaires | S | https://www.aspi.cz/opispdf/1921/003-1921.pdf | Medium (title) |
| 11 | 1927 | 104/1927 Sb., vl. nař., o služebních kategoriích vojenských a četnických důstojníků | Paraphrase: defines the service categories of military (and gendarmerie) officers — refines the rank-class architecture of 195/1920. Amended by 147/1930. | Rank/category structure (refinement). | ČS army & gendarmerie officers | H | https://www.aspi.cz/opispdf/1927/048-1927.pdf | Medium (title) |
| 12 | 1927 | 141/1927 Sb., vl. nař., jímž se vydávají nové branné předpisy | Paraphrase: re-issues the consolidated military regulations (≈14 MB) — the standing rulebook within which ranks/appointments operate. | Consolidated rank/appointment regulations. | All | H, A | https://www.aspi.cz/opispdf/1927/064-1927.pdf | Medium (title) |
| 13 | 1934 | 29 & 30/1934 Sb., zákon, jímž se mění některá ustanovení branného zákona + vyhláška o úplném znění branného zákona | Paraphrase: amends and republishes the consolidated text of the Branný zákon. | Confirms framework essentially stable; no new rank-conversion logic. | All | (H) | https://www.aspi.cz/opispdf/1934/014-1934.pdf | Medium (title) |
| 14 | 1934 | 279/1934 Sb., zákon, o započítávání vojenské služby v některých služebních poměrech | Paraphrase: late refinement of how military service is credited in certain (mostly civil) service relationships. | Service-time crediting (refinement). | State/military employees | T | https://www.aspi.cz/opispdf/1934/097-1934.pdf | Medium (title) |
Adjacent military laws found in the sweep but not about rank translation (welfare, courts, conscription strength, requisitioning, state-defence 1936–38, etc.) were logged and excluded — e.g. 48/1921, 58/1921, 76/1922, 370/1922, 154/1923, 150/1926, 51–54/1927, 131/1936, 184/1937.
2. Rank hierarchy that can be extracted
(a) Statutory rank-classes (hodnostní třídy) — 195/1920 Sb., §1, [OCR-verbatim structure]. Officers and military officials are placed in classes II (highest) to XI (lowest), each with a pay scale; NCOs and gážisté outside the rank classes sit in a separate scale (Díl II, pay classes 1–12). This class numbering is the legally authoritative ordering for pay and seniority.
(b) Named rank ladder in use 1918–1937 (conventional, cross-checked against a secondary source — verify against 141/1927 / 104/1927 for exact validity dates):
Mužstvo: vojín → svobodník → desátník → četař · Poddůstojníci: rotný (from 1923) → rotmistr → štábní rotmistr · Praporčík → podporučík → poručík → nadporučík → kapitán → štábní kapitán · Důstojníci vyšší: major → podplukovník → plukovník · Generálové (general titles from 1930): brigádní generál → divisní generál → armádní generál.
Insignia moved from collar badges to shoulder boards in 1921; general ranks were restructured 1930. (Confidence: medium — named ladder is secondary; rank-classes are statutory.)
3. Analytical summary — the legal logic, and legionnaires vs. former Habsburg soldiers
Two distinct legal questions, two distinct regimes. The post-1918 Sbírka does not contain a single "rank-conversion table." Instead it splits the problem:
- Who is let into the new army at all? — governed by the Branný zákon (193/1920) and, for ex-imperial officers, by 194/1920.
- Once in, where do you sit? — governed by 195/1920, which fixes the rank-classes (II–XI) and, crucially, how much prior service counts toward seniority and promotion.
Former Austro-Hungarian officers — discretion, not recognition. 194/1920 §1 gives the Ministry of National Defence the bare right to admit ex-Habsburg/Austrian/Hungarian gážisté "podle vlastního uvážení" (at its own discretion). There is no automatic recognition or conversion of an imperial rank into a Czechoslovak one; admission is a screening decision. Those not taken over are routed to pension/severance (zaopatřovací nároky), and the same law carves out a punitive track for officers "kteří se na československém národě provinili" (who offended against the Czechoslovak nation) — a loyalty filter written into the statute itself. Their prior service time, however, was not erased: under 195/1920 §6 the Habsburg war years (1915–1919) and even time in captivity count toward seniority once a man is admitted. So the Habsburg officer faced a gate (discretionary, loyalty-screened) followed by fair time-crediting — but no guaranteed rank.
Legionnaires — entitlement and back-dating. The legionnaire regime is built on entitlement. 282/1919 lets the government waive the ordinary formal qualifications; 462/1919 grants a preferential right and guarantee to state posts (≥50 % reserved) and even writes the legal definition of "legionnaire" into statute. Inside the army, 195/1920 §6(B) counts a legionnaire officer's service from 24 July 1914 — the earliest possible anchor — which mechanically pushes legionnaires up the seniority order relative to men whose clocks start later. The Russian-legion return date (8/1921) fixes the reference point for the largest cohort.
Net asymmetry. For the same underlying act — prior military service translated into a place in the new army — the law rewarded legionary service (waivers, reserved posts, seniority back-dated to 1914) and merely tolerated Austro-Hungarian service (discretionary admission, loyalty screen, pension if rejected, time-credit only after the gate). This is consistent with the political founding myth of the Republic and is itself a usable institutional fact for the thesis.
Stability over time. The architecture was complete by 1921. 1927 (104/1927 service categories; 141/1927 consolidated branné předpisy) and 1934 (29–30/1934 consolidation; 279/1934 service crediting) only refine it. From 1933 onward the sweep returns nothing on rank conversion — the rules were settled early, exactly as the late material confirms.
Scope caveat for the thesis. These statutes govern Legion → ČS army and Habsburg → ČS army transitions after 1918. They are not the rules behind the thesis's headline rank-reset variable, which concerns Habsburg → Legion rank on enrolment during the war (set by each legion's own wartime regulations in Russia/France/Italy, not by the Sbírka). Useful here as institutional context and as evidence that the state's revealed valuation of legionary vs. imperial service was sharply asymmetric — but cite it for the right link in the chain.
4. What still needs verification
- OCR'd but worth a second human read: the later §§ of 194/1920 (disloyalty/severance
wording) and 195/1920 §§6–7 (exact multipliers / which years double-count) — transcribed from
scans; the structure is solid, a few clause-level words are best re-checked against the page
images in the scratchpad (
…/scratchpad/ocr/cz1920_041-10..14.png,cz1919_060/093). - Title-only (not OCR'd), confidence medium: 457/1919, 222/1920, 269/1921, 440/1921, 8/1921, 104/1927, 141/1927, 29–30/1934, 279/1934 — verify exact titles/§§ before quoting.
- Named rank ladder (§2b): secondary source; confirm validity dates against 141/1927.
- Implementing decree 151/1920 (to 462/1919) reportedly sets the triple counting of legion service for civil-service seniority — quoted second-hand here; pull the text if that multiplier is cited in the thesis.
Source index (authoritative yearly lists)
ASPI opispdf yearly indexes used for the 1918–1938 sweep:
https://www.aspi.cz/opispdf/opispdf/[YEAR].html (1918 … 1938). Częstka PDFs cited per row above.
Free full-text mirrors (zakonyprolidi.cz, ASPI lawText) are bot-blocked or paywalled; psp.cz
digital library holds parliamentary prints for cross-checking the explanatory memoranda.
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 / formation | Records | % |
|---|---|---|
| CS Legion (Russia) | 85,087 | 64.0% |
| CS Legion (Italy) | 28,458 | 21.4% |
| CS Legion (France) | 9,700 | 7.3% |
| — (none recorded) | 5,486 | 4.1% |
| Russian Army | 2,124 | 1.6% |
| Serbian Legion | 855 | 0.6% |
| French Foreign Legion | 460 | 0.3% |
| British Army | 306 | 0.2% |
| French Army | 161 | 0.1% |
| Russian Army (Kornilov) | 83 | 0.1% |
| US Army | 79 | 0.1% |
| Polish Legion | 41 | 0.0% |
| Canadian Army | 34 | 0.0% |
| Romanian Army | 28 | 0.0% |
| Czechoslovak Army (post-1918) | 23 | 0.0% |
| Australian Army | 13 | 0.0% |
| Italská armáda | 11 | 0.0% |
| Kolčakova armáda | 7 | 0.0% |
| MRA | 6 | 0.0% |
| Děnikinova armáda | 4 | 0.0% |
| Domobrana v Itálii | 4 | 0.0% |
| DM | 3 | 0.0% |
| LP | 2 | 0.0% |
| LLR | 2 | 0.0% |
| ČH | 2 | 0.0% |
| L | 2 | 0.0% |
| S | 1 | 0.0% |
| ČNR | 1 | 0.0% |
| lLR | 1 | 0.0% |
| LER | 1 | 0.0% |
| LKR | 1 | 0.0% |
| v | 1 | 0.0% |
| RM | 1 | 0.0% |
| Lf | 1 | 0.0% |
| S X | 1 | 0.0% |
| 2.stř.pl. | 1 | 0.0% |
| BEL | 1 | 0.0% |
| LRF | 1 | 0.0% |
The three columns, and which to use
| Column | What it is | Use for |
|---|---|---|
regiment | display label, army stripped (6,058 distinct) | reading, plot facets |
legion_army | the formation, normalised | splitting Legion from foreign service |
unit_key | regiment @ 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.
| Status | Records | % of records | Deserters |
|---|---|---|---|
| Verified — number exists in this theatre | 71,588 | 53.8% | 2,136 |
| No Fidler entry for this number | 2,558 | 1.9% | 0 |
| Conflict — number never existed in this theatre | 266 | 0.2% | 0 |
| Not checkable (support unit / foreign army / no number) | 58,581 | 44.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 label | Tagged theatre | Records |
|---|---|---|
| 23.stř.pl. | Russia | 34 |
| 21.stř.pl. | Russia | 28 |
| 23.p.pl. | Russia | 27 |
| 21.p.pl. | Russia | 25 |
| 33.p.pl. | France | 17 |
| 4.p.pl. | Italy | 16 |
| 22.stř.pl. | Russia | 9 |
| 33.p.pl. | Russia | 9 |
| 3.p.pl. | Italy | 9 |
| 1.p.pl. | Italy | 7 |
| 23.p.pl. | Italy | 6 |
| 32.p.pl. | Russia | 6 |
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
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_capturevalues 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 inehnl_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.csvonto the person panel viahabsburg_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.
| Branch | Type | No. | Recruiting district | Dominant language | % Czech | Homogeneity % | 1910 district |
|---|---|---|---|---|---|---|---|
| Infantry | common | 1 | Opava | German | 0 | 82 | Opava |
| Infantry | common | 2 | Kronstadt (Brassó) | Magyar | 0 | 61 | — |
| Infantry | common | 3 | Kroměříž | Czech | 83 | 83 | Kroměříž |
| Infantry | common | 4 | Wien | German | 0 | 95 | — |
| Infantry | common | 5 | Szatmár-Németi | Magyar | 0 | 58 | — |
| Infantry | common | 6 | Neusatz (Ujvidek) | German | 0 | 41 | — |
| Infantry | common | 7 | Klagenfurt | German | 0 | 79 | — |
| Infantry | common | 8 | Brno | Czech | 67 | 67 | Brno |
| Infantry | common | 9 | Stryi | Ruthenian | 0 | 73 | — |
| Infantry | common | 10 | Przemyśl | Ruthenian | 0 | 47 | — |
| Infantry | common | 11 | Písek | Czech | 79 | 79 | Písek |
| Infantry | common | 12 | Komárno | Magyar | 31 | 58 | — |
| Infantry | common | 13 | Kraków | Polish | 0 | 82 | — |
| Infantry | common | 14 | Linz | German | 0 | 98 | — |
| Infantry | common | 15 | Ternopil | Ruthenian | 0 | 62 | — |
| Infantry | common | 16 | Belovar | Croat-Serb | 0 | 97 | — |
| Infantry | common | 17 | Ljubljana | Slovene | 0 | 86 | — |
| Infantry | common | 18 | Hradec Králové | Czech | 75 | 75 | Hradec Králové |
| Infantry | common | 19 | Raab (Györ) | Magyar | 0 | 95 | — |
| Infantry | common | 20 | Nowy Sącz | Polish | 0 | 86 | — |
| Infantry | common | 21 | Čáslav | Czech | 87 | 87 | Čáslav |
| Infantry | common | 22 | Sinj | Croat-Serb | 0 | 82 | — |
| Infantry | common | 23 | Zombor | Magyar | 0 | 52 | — |
| Infantry | common | 24 | Kolomyja | Ruthenian | 0 | 79 | — |
| Infantry | common | 25 | Lučenec | Magyar | 0 | 56 | Losonci |
| Infantry | common | 26 | Gran (Esztergom) | Magyar | 0 | 53 | Esztergomi |
| Infantry | common | 27 | Graz | German | 0 | 94 | — |
| Infantry | common | 28 | Praha | Czech | 95 | 95 | Praha |
| Infantry | common | 29 | Nagy Becskerek | Croat-Serb | 0 | 44 | — |
| Infantry | common | 30 | Lviv | Ruthenian | 0 | 59 | — |
| Infantry | common | 31 | Hermannstadt (Nagyszeben) | Romanian | 0 | 69 | — |
| Infantry | common | 32 | Budapest | Magyar | 0 | 91 | — |
| Infantry | common | 33 | Arad | Romanian | 0 | 54 | — |
| Infantry | common | 34 | Košice | Magyar | 0 | 91 | Kassai |
| Infantry | common | 35 | Plzeň | Czech | 60 | 60 | Plzeň |
| Infantry | common | 36 | Mladá Boleslav | Czech | 95 | 95 | Mladá Boleslav |
| Infantry | common | 37 | Großwardein (Nagy Várad) | Romanian | 0 | 49 | — |
| Infantry | common | 38 | Kecskemét | Magyar | 0 | 97 | — |
| Infantry | common | 39 | Debrezin (Debreczen) | Magyar | 0 | 92 | — |
| Infantry | common | 40 | Rzeszów | Polish | 0 | 97 | — |
| Infantry | common | 41 | Chernivtsi | Romanian | 0 | 54 | — |
| Infantry | common | 42 | Terezín | German | 0 | 86 | — |
| Infantry | common | 43 | Karánsebes | Romanian | 0 | 78 | — |
| Infantry | common | 44 | Kaposvár | Magyar | 0 | 88 | — |
| Infantry | common | 45 | Sanok | Ruthenian | 0 | 47 | — |
| Infantry | common | 46 | Szegedin (Szeged) | Magyar | 0 | 79 | — |
| Infantry | common | 47 | Maribor | German | 0 | 77 | — |
| Infantry | common | 48 | Groß-(Nagy-)Kanizsa | Magyar | 0 | 82 | — |
| Infantry | common | 49 | Sankt Pölten | German | 0 | 98 | — |
| Infantry | common | 50 | Karlsburg (Gyulafehérvár) | Romanian | 0 | 71 | — |
| Infantry | common | 51 | Klausenburg (Kolozsvár) | Romanian | 0 | 72 | — |
| Infantry | common | 52 | Fünfkirchen (Pécs) | Magyar | 0 | 52 | — |
| Infantry | common | 53 | Agram | Croat-Serb | 0 | 97 | — |
| Infantry | common | 54 | Olomouc | Czech | 64 | 64 | Olomouc |
| Infantry | common | 55 | Berezhany | Ruthenian | 0 | 59 | — |
| Infantry | common | 56 | Wadowice | Polish | 0 | 88 | — |
| Infantry | common | 57 | Tarnów | Polish | 0 | 91 | — |
| Infantry | common | 58 | Ivano-Frankivsk | Ruthenian | 0 | 72 | — |
| Infantry | common | 59 | Salzburg | German | 0 | 97 | — |
| Infantry | common | 60 | Erlau (Eger) | Magyar | 0 | 98 | — |
| Infantry | common | 61 | Temesvár | Romanian | 0 | 38 | — |
| Infantry | common | 62 | Maros-Vásárhely | Magyar | 0 | 49 | — |
| Infantry | common | 63 | Bistritz (Beszterce) | Romanian | 0 | 73 | — |
| Infantry | common | 64 | Broos (Szászváros) | Romanian | 0 | 86 | — |
| Infantry | common | 65 | Mukačevo | Magyar | 0 | 83 | Munkácsi |
| Infantry | common | 66 | Užhorod | Slovak | 0 | 46 | Ungvári |
| Infantry | common | 67 | Prešov | Slovak | 0 | 70 | Eperjesi |
| Infantry | common | 68 | Szolnok | Magyar | 0 | 98 | — |
| Infantry | common | 69 | Stuhlweißenburg (Székesfehérvár) | Magyar | 0 | 92 | — |
| Infantry | common | 70 | Peterwardein | Croat-Serb | 0 | 79 | — |
| Infantry | common | 71 | Trenčín | Slovak | 0 | 85 | Trencséni |
| Infantry | common | 72 | Bratislava | Slovak | 0 | 51 | Pozsonyi |
| Infantry | common | 73 | Cheb | German | 0 | 97 | Cheb |
| Infantry | common | 74 | Jičín | Czech | 63 | 63 | Jičín |
| Infantry | common | 75 | Jindřichův Hradec | Czech | 79 | 79 | Jindřichův Hradec |
| Infantry | common | 76 | Ödenburg (Sopron) | German | 0 | 54 | — |
| Infantry | common | 77 | Sambir | Ruthenian | 0 | 69 | — |
| Infantry | common | 78 | Esseg | Croat-Serb | 0 | 84 | — |
| Infantry | common | 79 | Otočac | Croat-Serb | 0 | 96 | — |
| Infantry | common | 80 | Zolochiv | Ruthenian | 0 | 68 | — |
| Infantry | common | 81 | Jihlava | Czech | 69 | 69 | Jihlava |
| Infantry | common | 82 | Székely Udvarhely | Magyar | 0 | 88 | — |
| Infantry | common | 83 | Steinamanger (Szombathely) | Magyar | 0 | 55 | — |
| Infantry | common | 84 | Wien | German | 0 | 97 | — |
| Infantry | common | 85 | Máramaros Sziget | Ruthenian | 0 | 33 | Szigeti |
| Infantry | common | 86 | Maria Theresiopel (Szabatka) | Magyar | 0 | 76 | — |
| Infantry | common | 87 | Celje | Slovene | 0 | 86 | — |
| Infantry | common | 88 | Beroun | Czech | 72 | 72 | Hořovice |
| Infantry | common | 89 | Gródek-Jagielloński | Ruthenian | 0 | 60 | — |
| Infantry | common | 90 | Jarosław | Polish | 0 | 75 | — |
| Infantry | common | 91 | České Budějovice | German | 45 | 54 | České Budějovice |
| Infantry | common | 92 | Chomutov | German | 0 | 80 | Chomutov |
| Infantry | common | 93 | Šumperk | German | 35 | 60 | Šumperk |
| Infantry | common | 94 | Turnov | German | 22 | 76 | Turnov |
| Infantry | common | 95 | Chortkiv | Ruthenian | 0 | 70 | — |
| Infantry | common | 96 | Carlstadt | Croat-Serb | 0 | 97 | — |
| Infantry | common | 97 | Trieste | Slovene | 0 | 45 | — |
| Infantry | common | 98 | Vysoké Mýto | Czech | 68 | 68 | Vysoké Mýto |
| Infantry | common | 99 | Znojmo | German | 37 | 60 | Znojmo |
| Infantry | common | 100 | Těšín | Polish | 33 | 37 | Těšín |
| Infantry | common | 101 | Békéscsaba | Magyar | 0 | 84 | — |
| Infantry | common | 102 | Benešov | Czech | 91 | 91 | Benešov |
| Infantry | Landwehr | 1 | Wien | German | 0 | 95 | — |
| Infantry | Landwehr | 2 | Linz; Salzburg | German | 0 | 98 | — |
| Infantry | Landwehr | 3 | Graz; Maribor | German | 0 | 94 | — |
| Infantry | Landwehr | 4 | Klagenfurt | German | 0 | 79 | — |
| Infantry | Landwehr | 5 | Trieste | Slovene | 0 | 45 | — |
| Infantry | Landwehr | 6 | Cheb; Beroun | German | 0 | 97 | Cheb |
| Infantry | Landwehr | 7 | Plzeň; Beroun; Písek | Czech | 60 | 60 | Plzeň |
| Infantry | Landwehr | 8 | Praha; Beroun | Czech | 95 | 95 | Praha |
| Infantry | Landwehr | 9 | Litoměřice; Chomutov | German | 0 | 86 | Litoměřice |
| Infantry | Landwehr | 10 | Mladá Boleslav; Turnov | Czech | 95 | 95 | Mladá Boleslav |
| Infantry | Landwehr | 11 | Jičín; Hradec Králové | Czech | 63 | 63 | Jičín |
| Infantry | Landwehr | 12 | Čáslav; Mladá Boleslav | Czech | 87 | 87 | Čáslav |
| Infantry | Landwehr | 13 | Olomouc; Šumperk | Czech | 64 | 64 | Olomouc |
| Infantry | Landwehr | 14 | Brno; Jihlava | Czech | 67 | 67 | Brno |
| Infantry | Landwehr | 15 | Opava; Olomouc | German | 0 | 82 | Opava |
| Infantry | Landwehr | 16 | Kraków; Wadowice | Polish | 0 | 82 | — |
| Infantry | Landwehr | 17 | Rzeszów | Polish | 0 | 97 | — |
| Infantry | Landwehr | 18 | Przemyśl; Sanok | Ruthenian | 0 | 47 | — |
| Infantry | Landwehr | 19 | Lviv; Berezhany | Ruthenian | 0 | 59 | — |
| Infantry | Landwehr | 20 | Ivano-Frankivsk; Berezhany; Chortkiv | Ruthenian | 0 | 72 | — |
| Infantry | Landwehr | 21 | Sankt Pölten; Wien | German | 0 | 98 | — |
| Infantry | Landwehr | 22 | Chernivtsi; Kolomyja | Romanian | 0 | 54 | — |
| Infantry | Landwehr | 23 | Sebenico | Croat-Serb | 0 | 82 | — |
| Infantry | Landwehr | 24 | Wien; Znojmo | German | 0 | 97 | Znojmo |
| Infantry | Landwehr | 25 | Kroměříž | Czech | 83 | 83 | Kroměříž |
| Infantry | Landwehr | 26 | Maribor; Celje | German | 0 | 77 | — |
| Infantry | Landwehr | 27 | Ljubljana; Trieste | Slovene | 0 | 86 | — |
| Infantry | Landwehr | 28 | Písek; Jindřichův Hradec; Benešov | Czech | 79 | 79 | Písek |
| Infantry | Landwehr | 29 | České Budějovice; Písek | German | 45 | 54 | České Budějovice |
| Infantry | Landwehr | 30 | Vysoké Mýto; Hradec Králové | Czech | 68 | 68 | Vysoké Mýto |
| Infantry | Landwehr | 31 | Těšín; Wadowice | German | 33 | 37 | Těšín |
| Infantry | Landwehr | 32 | Nowy Sącz; Tarnów | Polish | 0 | 91 | — |
| Infantry | Landwehr | 33 | Stryi; Sambir | Ruthenian | 0 | 73 | — |
| Infantry | Landwehr | 34 | Jarosław; Gródek-Jagielloński | Polish | 0 | 75 | — |
| Infantry | Landwehr | 35 | Zolochiv; Ternopil | Ruthenian | 0 | 68 | — |
| Infantry | Landwehr | 36 | Kolomyja; Ivano-Frankivsk; Chortkiv | Ruthenian | 0 | 70 | — |
| Infantry | Landwehr | 37 | Castelnuovo | Croat-Serb | 0 | 82 | — |
| Infantry | Honvéd | 1 | Budapest | Magyar | 0 | 91 | — |
| Infantry | Honvéd | 2 | Gyula; Arad | Romanian | 0 | 54 | — |
| Infantry | Honvéd | 3 | Debrecin | Magyar | 0 | 92 | — |
| Infantry | Honvéd | 4 | Großwardein | Romanian | 0 | 49 | — |
| Infantry | Honvéd | 5 | Szegedin; Großbecskerek | Magyar | 0 | 79 | — |
| Infantry | Honvéd | 6 | Maria-Theresiopel; Zombor; Neusatz | Magyar | 0 | 76 | — |
| Infantry | Honvéd | 7 | Werschetz | Romanian | 0 | 44 | — |
| Infantry | Honvéd | 8 | Lugos | Romanian | 0 | 78 | — |
| Infantry | Honvéd | 9 | Košice; Spišská Nová Ves | Magyar | 0 | 91 | Iglói |
| Infantry | Honvéd | 10 | Erlau | Magyar | 0 | 98 | — |
| Infantry | Honvéd | 11 | Mukačevo; Užhorod | Magyar | 0 | 42 | Munkácsi |
| Infantry | Honvéd | 12 | Szatmár-Németi; Máramarossziget | Magyar | 0 | 58 | Szigeti |
| Infantry | Honvéd | 13 | Bratislava | Slovak | 0 | 51 | Pozsonyi |
| Infantry | Honvéd | 14 | Nitra; Levice | Slovak | 0 | 85 | Lévai |
| Infantry | Honvéd | 15 | Trenčín | Slovak | 0 | 85 | Trencséni |
| Infantry | Honvéd | 16 | Banská Bystrica; Jelšava | Magyar | 0 | 56 | Besztercebányai |
| Infantry | Honvéd | 17 | Stuhlweißenburg; Kaposvár | Magyar | 0 | 92 | — |
| Infantry | Honvéd | 18 | Ödenburg; Güns | German | 0 | 54 | — |
| Infantry | Honvéd | 19 | Fünfkirchen; Kaposvár; Fiume | Magyar | 0 | 52 | — |
| Infantry | Honvéd | 20 | Groß-Kanizsa | Magyar | 0 | 82 | — |
| Infantry | Honvéd | 21 | Klausenburg; Nagyenyed | Romanian | 0 | 62 | — |
| Infantry | Honvéd | 22 | Maros-Vásárhely | Magyar | 0 | 49 | — |
| Infantry | Honvéd | 23 | Hermannstadt; Déva | Romanian | 0 | 69 | — |
| Infantry | Honvéd | 24 | Kronstadt; Czikszereda | Magyar | 0 | 61 | — |
| Infantry | Honvéd | 25 | Agram; Warasdin | Croat-Serb | 0 | 97 | — |
| Infantry | Honvéd | 26 | Carlstadt; Gospić | Croat-Serb | 0 | 97 | — |
| Infantry | Honvéd | 27 | Esseg | Croat-Serb | 0 | 84 | — |
| Infantry | Honvéd | 28 | Esseg | Croat-Serb | 0 | 96 | — |
| Infantry | Honvéd | 29 | Budapest; Jászberény | Magyar | 0 | 91 | — |
| Infantry | Honvéd | 30 | Budapest | Magyar | 0 | 91 | — |
| Infantry | Honvéd | 31 | Veßprim | Magyar | 0 | 92 | — |
| Infantry | Honvéd | 32 | Bistritz | Romanian | 0 | 73 | — |
| Infantry | special | 1 | Innsbruck; Bressanone; Trento | German | 0 | 58 | — |
| Infantry | special | 2 | Innsbruck; Bressanone; Trento | German | 0 | 55 | — |
| Infantry | special | 3 | Innsbruck; Bressanone; Trento | German | 0 | 59 | — |
| Infantry | special | 4 | Innsbruck; Bressanone; Trento | German | 0 | 59 | — |
| Infantry | special | 1 | Sarajevo | Croat-Serb | 0 | 94 | — |
| Infantry | special | 2 | Banja Luka | Croat-Serb | 0 | 93 | — |
| Infantry | special | 3 | Tuzla | Croat-Serb | 0 | 94 | — |
| Infantry | special | 4 | Mostar | Croat-Serb | 0 | 95 | — |
| Feldjäger | Feldjäger | 1 | nan | German | 36 | 62 | — |
| Feldjäger | Feldjäger | 2 | nan | Czech | 74 | 74 | — |
| Feldjäger | Feldjäger | 4 | nan | Polish | 0 | 77 | — |
| Feldjäger | Feldjäger | 5 | nan | German | 25 | 39 | — |
| Feldjäger | Feldjäger | 6 | nan | Czech | 69 | 69 | — |
| Feldjäger | Feldjäger | 7 | nan | Slovene | 0 | 85 | — |
| Feldjäger | Feldjäger | 8 | nan | German | 0 | 78 | — |
| Feldjäger | Feldjäger | 9 | nan | German | 0 | 96 | — |
| Feldjäger | Feldjäger | 10 | nan | German | 0 | 98 | — |
| Feldjäger | Feldjäger | 11 | nan | German | 0 | 52 | — |
| Feldjäger | Feldjäger | 12 | nan | Czech | 67 | 67 | — |
| Feldjäger | Feldjäger | 13 | nan | Polish | 0 | 47 | — |
| Feldjäger | Feldjäger | 14 | nan | Ruthenian | 0 | 47 | — |
| Feldjäger | Feldjäger | 16 | nan | German | 34 | 56 | — |
| Feldjäger | Feldjäger | 17 | nan | Czech | 63 | 63 | — |
| Feldjäger | Feldjäger | 18 | nan | Ruthenian | 0 | 59 | — |
| Feldjäger | Feldjäger | 19 | nan | Slovak | 0 | 58 | — |
| Feldjäger | Feldjäger | 20 | nan | Slovene | 0 | 48 | — |
| Feldjäger | Feldjäger | 21 | nan | German | 0 | 98 | — |
| Feldjäger | Feldjäger | 22 | nan | German | 49 | 50 | — |
| Feldjäger | Feldjäger | 23 | nan | Romanian | 0 | 68 | — |
| Feldjäger | Feldjäger | 24 | nan | Magyar | 0 | 86 | — |
| Feldjäger | Feldjäger | 25 | nan | Czech | 75 | 75 | — |
| Feldjäger | Feldjäger | 27 | nan | Romanian | 0 | 54 | — |
| Feldjäger | Feldjäger | 28 | nan | Romanian | 0 | 73 | — |
| Feldjäger | Feldjäger | 29 | nan | Slovak | 0 | 67 | — |
| Feldjäger | Feldjäger | 30 | nan | Ruthenian | 0 | 70 | — |
| Feldjäger | Feldjäger | 31 | nan | Croat-Serb | 0 | 95 | — |
| Feldjäger | Feldjäger | 32 | nan | Slovak | 0 | 74 | — |
| Feldjäger | Feldjäger | 1 | nan | Croat-Serb | 0 | 96 | — |
| Cavalry | Dragoner | 1 | nan | Czech | 48 | 48 | — |
| Cavalry | Dragoner | 2 | nan | Czech | 61 | 61 | — |
| Cavalry | Dragoner | 3 | nan | German | 0 | 97 | — |
| Cavalry | Dragoner | 4 | nan | German | 0 | 99 | — |
| Cavalry | Dragoner | 5 | nan | Slovene | 0 | 51 | — |
| Cavalry | Dragoner | 6 | nan | Czech | 61 | 61 | — |
| Cavalry | Dragoner | 7 | nan | German | 50 | 50 | — |
| Cavalry | Dragoner | 8 | nan | Czech | 58 | 58 | — |
| Cavalry | Dragoner | 9 | nan | Romanian | 0 | 50 | — |
| Cavalry | Dragoner | 10 | nan | Czech | 62 | 62 | — |
| Cavalry | Dragoner | 11 | nan | Czech | 90 | 90 | — |
| Cavalry | Dragoner | 12 | nan | Czech | 50 | 50 | — |
| Cavalry | Dragoner | 13 | nan | Czech | 51 | 51 | — |
| Cavalry | Dragoner | 14 | nan | Czech | 59 | 59 | — |
| Cavalry | Dragoner | 15 | nan | German | 0 | 93 | — |
| Cavalry | Husar | 1 | nan | Magyar | 0 | 85 | — |
| Cavalry | Husar | 2 | nan | Magyar | 0 | 89 | — |
| Cavalry | Husar | 3 | nan | Magyar | 0 | 68 | — |
| Cavalry | Husar | 4 | nan | Magyar | 0 | 71 | — |
| Cavalry | Husar | 5 | nan | Magyar | 0 | 90 | — |
| Cavalry | Husar | 6 | nan | Magyar | 0 | 90 | — |
| Cavalry | Husar | 7 | nan | Magyar | 0 | 98 | — |
| Cavalry | Husar | 8 | nan | Magyar | 0 | 74 | — |
| Cavalry | Husar | 9 | nan | Magyar | 0 | 81 | — |
| Cavalry | Husar | 10 | nan | Magyar | 0 | 97 | — |
| Cavalry | Husar | 11 | nan | Magyar | 0 | 96 | — |
| Cavalry | Husar | 12 | nan | Magyar | 0 | 96 | — |
| Cavalry | Husar | 13 | nan | Magyar | 0 | 97 | — |
| Cavalry | Husar | 14 | nan | Magyar | 0 | 92 | — |
| Cavalry | Husar | 15 | nan | Magyar | 0 | 91 | — |
| Cavalry | Husar | 16 | nan | Magyar | 0 | 94 | — |
| Cavalry | Ulan | 1 | nan | Polish | 0 | 85 | — |
| Cavalry | Ulan | 2 | nan | Polish | 0 | 84 | — |
| Cavalry | Ulan | 3 | nan | Polish | 0 | 69 | — |
| Cavalry | Ulan | 4 | nan | Ruthenian | 0 | 65 | — |
| Cavalry | Ulan | 5 | nan | Croat-Serb | 0 | 97 | — |
| Cavalry | Ulan | 6 | nan | Polish | 0 | 52 | — |
| Cavalry | Ulan | 7 | nan | Ruthenian | 0 | 72 | — |
| Cavalry | Ulan | 8 | nan | Ruthenian | 0 | 80 | — |
| Cavalry | Ulan | 11 | nan | Czech | 65 | 65 | — |
| Cavalry | Ulan | 12 | nan | Croat-Serb | 0 | 81 | — |
| Cavalry | Ulan | 13 | nan | Ruthenian | 0 | 55 | — |
| Artillery | Feldkanonen | 1 | nan | Polish | 0 | 49 | — |
| Artillery | Feldkanonen | 2 | nan | German | 32 | 32 | — |
| Artillery | Feldkanonen | 3 | nan | Polish | 20 | 47 | — |
| Artillery | Feldkanonen | 4 | nan | German | 0 | 83 | — |
| Artillery | Feldkanonen | 5 | nan | Czech | 68 | 68 | — |
| Artillery | Feldkanonen | 6 | nan | German | 0 | 85 | — |
| Artillery | Feldkanonen | 7 | nan | Slovene | 0 | 64 | — |
| Artillery | Feldkanonen | 8 | nan | Slovene | 0 | 56 | — |
| Artillery | Feldkanonen | 9 | nan | German | 0 | 71 | — |
| Artillery | Feldkanonen | 10 | nan | Magyar | 0 | 72 | — |
| Artillery | Feldkanonen | 11 | nan | Magyar | 0 | 77 | — |
| Artillery | Feldkanonen | 12 | nan | Magyar | 0 | 83 | — |
| Artillery | Feldkanonen | 13 | nan | Magyar | 0 | 44 | — |
| Artillery | Feldkanonen | 14 | nan | Slovak | 0 | 62 | — |
| Artillery | Feldkanonen | 15 | nan | Magyar | 0 | 57 | — |
| Artillery | Feldkanonen | 16 | nan | Magyar | 0 | 64 | — |
| Artillery | Feldkanonen | 17 | nan | Magyar | 0 | 86 | — |
| Artillery | Feldkanonen | 18 | nan | Magyar | 0 | 65 | — |
| Artillery | Feldkanonen | 19 | nan | Magyar | 0 | 57 | — |
| Artillery | Feldkanonen | 20 | nan | German | 0 | 40 | — |
| Artillery | Feldkanonen | 21 | nan | Romanian | 0 | 44 | — |
| Artillery | Feldkanonen | 22 | nan | German | 39 | 60 | — |
| Artillery | Feldkanonen | 23 | nan | Czech | 72 | 72 | — |
| Artillery | Feldkanonen | 24 | nan | Czech | 70 | 70 | — |
| Artillery | Feldkanonen | 25 | nan | Czech | 62 | 62 | — |
| Artillery | Feldkanonen | 26 | nan | German | 43 | 55 | — |
| Artillery | Feldkanonen | 27 | nan | Czech | 66 | 66 | — |
| Artillery | Feldkanonen | 28 | nan | Ruthenian | 0 | 60 | — |
| Artillery | Feldkanonen | 29 | nan | Polish | 0 | 78 | — |
| Artillery | Feldkanonen | 30 | nan | Ruthenian | 0 | 66 | — |
| Artillery | Feldkanonen | 31 | nan | Ruthenian | 0 | 69 | — |
| Artillery | Feldkanonen | 32 | nan | Ruthenian | 0 | 55 | — |
| Artillery | Feldkanonen | 33 | nan | Ruthenian | 0 | 43 | — |
| Artillery | Feldkanonen | 34 | nan | Magyar | 0 | 60 | — |
| Artillery | Feldkanonen | 35 | nan | Romanian | 0 | 41 | — |
| Artillery | Feldkanonen | 36 | nan | Romanian | 0 | 44 | — |
| Artillery | Feldkanonen | 37 | nan | Croat-Serb | 0 | 88 | — |
| Artillery | Feldkanonen | 38 | nan | Croat-Serb | 0 | 63 | — |
| Artillery | Feldkanonen | 39 | nan | Croat-Serb | 0 | 66 | — |
| Artillery | Feldkanonen | 40 | nan | German | 0 | 93 | — |
| Artillery | Feldkanonen | 41 | nan | German | 0 | 98 | — |
| Artillery | Feldkanonen | 42 | nan | German | 0 | 86 | — |
| Artillery | Feldhaubitz | 1 | nan | Polish | 0 | 44 | — |
| Artillery | Feldhaubitz | 2 | nan | German | 29 | 67 | — |
| Artillery | Feldhaubitz | 3 | nan | German | 0 | 80 | — |
| Artillery | Feldhaubitz | 4 | nan | Magyar | 0 | 79 | — |
| Artillery | Feldhaubitz | 5 | nan | Slovak | 0 | 63 | — |
| Artillery | Feldhaubitz | 6 | nan | Magyar | 0 | 75 | — |
| Artillery | Feldhaubitz | 7 | nan | German | 0 | 43 | — |
| Artillery | Feldhaubitz | 8 | nan | Czech | 88 | 88 | — |
| Artillery | Feldhaubitz | 9 | nan | Czech | 63 | 63 | — |
| Artillery | Feldhaubitz | 10 | nan | Polish | 0 | 76 | — |
| Artillery | Feldhaubitz | 11 | nan | Ruthenian | 0 | 52 | — |
| Artillery | Feldhaubitz | 12 | nan | Magyar | 0 | 39 | — |
| Artillery | Feldhaubitz | 13 | nan | Croat-Serb | 0 | 72 | — |
| Artillery | Feldhaubitz | 14 | nan | German | 0 | 97 | — |
| Artillery | reitende Artillerie | 1 | nan | Polish | 20 | 50 | — |
| Artillery | reitende Artillerie | 2 | nan | German | 28 | 70 | — |
| Artillery | reitende Artillerie | 4 | nan | Magyar | 0 | 87 | — |
| Artillery | reitende Artillerie | 5 | nan | Slovak | 0 | 70 | — |
| Artillery | reitende Artillerie | 6 | nan | Magyar | 0 | 80 | — |
| Artillery | reitende Artillerie | 7 | nan | Magyar | 0 | 40 | — |
| Artillery | reitende Artillerie | 9 | nan | German | 39 | 60 | — |
| Artillery | reitende Artillerie | 10 | nan | Polish | 0 | 57 | — |
| Artillery | reitende Artillerie | 11 | nan | Ruthenian | 0 | 57 | — |
| Artillery | schwere Haubitz | 1 | nan | Polish | 0 | 40 | — |
| Artillery | schwere Haubitz | 2 | nan | German | 28 | 68 | — |
| Artillery | schwere Haubitz | 3 | nan | Slovene | 0 | 48 | — |
| Artillery | schwere Haubitz | 4 | nan | Magyar | 0 | 78 | — |
| Artillery | schwere Haubitz | 5 | nan | Magyar | 0 | 55 | — |
| Artillery | schwere Haubitz | 6 | nan | Magyar | 0 | 72 | — |
| Artillery | schwere Haubitz | 7 | nan | Magyar | 0 | 35 | — |
| Artillery | schwere Haubitz | 8 | nan | Czech | 60 | 60 | — |
| Artillery | schwere Haubitz | 9 | nan | Czech | 54 | 54 | — |
| Artillery | schwere Haubitz | 10 | nan | Ruthenian | 0 | 46 | — |
| Artillery | schwere Haubitz | 11 | nan | Ruthenian | 0 | 46 | — |
| Artillery | schwere Haubitz | 12 | nan | Magyar | 0 | 41 | — |
| Artillery | schwere Haubitz | 13 | nan | Croat-Serb | 0 | 82 | — |
| Artillery | schwere Haubitz | 14 | nan | German | 0 | 95 | — |
| Artillery | Gebirgsartillerie | 3 | nan | Slovene | 0 | 48 | — |
| Artillery | Gebirgsartillerie | 4 | nan | Magyar | 0 | 78 | — |
| Artillery | Gebirgsartillerie | 6 | nan | Magyar | 0 | 72 | — |
| Artillery | Gebirgsartillerie | 7 | nan | Magyar | 0 | 35 | — |
| Artillery | Gebirgsartillerie | 8 | nan | Czech | 60 | 60 | — |
| Artillery | Gebirgsartillerie | 10 | nan | Ruthenian | 0 | 46 | — |
| Artillery | Gebirgsartillerie | 11 | nan | Ruthenian | 0 | 46 | — |
| Artillery | Gebirgsartillerie | 12 | nan | Magyar | 0 | 41 | — |
| Artillery | Gebirgsartillerie | 13 | nan | Croat-Serb | 0 | 82 | — |
| Artillery | Gebirgsartillerie | 14 | nan | German | 0 | 98 | — |
| Artillery | Gebirgsartillerie | nan | nan | Croat-Serb | 0 | 92 | — |
| Artillery | Festungsartillerie | 1 | nan | German | 23 | 74 | — |
| Artillery | Festungsartillerie | 2 | nan | Polish | 27 | 38 | — |
| Artillery | Festungsartillerie | 3 | nan | Ruthenian | 0 | 50 | — |
| Artillery | Festungsartillerie | 4 | nan | German | 0 | 35 | — |
| Artillery | Festungsartillerie | 5 | nan | Magyar | 0 | 26 | — |
| Artillery | Festungsartillerie | 6 | nan | Magyar | 0 | 54 | — |
| Artillery | Festungsartillerie | 1 | nan | German | 0 | 94 | — |
| Artillery | Festungsartillerie | 2 | nan | Romanian | 0 | 36 | — |
| Artillery | Festungsartillerie | 3 | nan | German | 0 | 40 | — |
| Artillery | Festungsartillerie | 4 | nan | German | 34 | 64 | — |
| Artillery | Festungsartillerie | 5 | nan | German | 31 | 32 | — |
| Artillery | Festungsartillerie | 6 | nan | German | 0 | 31 | — |
| Artillery | Festungsartillerie | 7 | nan | Magyar | 0 | 54 | — |
| Artillery | Festungsartillerie | 8 | nan | Slovene | 0 | 48 | — |
| Artillery | Festungsartillerie | 9 | nan | Czech | 60 | 60 | — |
| Artillery | Festungsartillerie | 10 | nan | German | 28 | 68 | — |
| Technical | Sappeur | 1 | nan | Polish | 23 | 50 | — |
| Technical | Sappeur | 2 | nan | German | 0 | 82 | — |
| Technical | Sappeur | 3 | nan | German | 0 | 48 | — |
| Technical | Sappeur | 4 | nan | Magyar | 0 | 74 | — |
| Technical | Sappeur | 5 | nan | German | 0 | 40 | — |
| Technical | Sappeur | 6 | nan | Magyar | 0 | 72 | — |
| Technical | Sappeur | 7 | nan | Magyar | 0 | 53 | — |
| Technical | Sappeur | 8 | nan | Czech | 73 | 73 | — |
| Technical | Sappeur | 9 | nan | Czech | 66 | 66 | — |
| Technical | Sappeur | 10 | nan | Polish | 0 | 50 | — |
| Technical | Sappeur | 11 | nan | Ruthenian | 0 | 48 | — |
| Technical | Sappeur | 12 | nan | Magyar | 0 | 50 | — |
| Technical | Sappeur | 13 | nan | Croat-Serb | 0 | 86 | — |
| Technical | Sappeur | 14 | nan | German | 0 | 98 | — |
| Technical | Pionier | 2 | nan | German | 0 | 98 | — |
| Technical | Pionier | 3 | nan | German | 0 | 48 | — |
| Technical | Pionier | 4 | nan | Magyar | 0 | 74 | — |
| Technical | Pionier | 5 | nan | Magyar | 0 | 55 | — |
| Technical | Pionier | 7 | nan | Magyar | 0 | 57 | — |
| Technical | Pionier | 8 | nan | Czech | 56 | 56 | — |
| Technical | Pionier | 9 | nan | Czech | 45 | 45 | — |
| Technical | Pionier | 10 | nan | Polish | 0 | 50 | — |
| Technical | Pionier | 15 | nan | Croat-Serb | 0 | 92 | — |
| Technical | Brücken | nan | nan | German | 0 | 98 | — |
| Technical | Verkehrstruppen | nan | nan | German | 20 | 30 | — |
| Technical | Train | 1 | nan | Polish | 0 | 57 | — |
| Technical | Train | 2 | nan | German | 42 | 48 | — |
| Technical | Train | 3 | nan | German | 0 | 56 | — |
| Technical | Train | 4 | nan | Magyar | 0 | 84 | — |
| Technical | Train | 5 | nan | Magyar | 0 | 53 | — |
| Technical | Train | 6 | nan | Magyar | 0 | 88 | — |
| Technical | Train | 7 | nan | Magyar | 0 | 57 | — |
| Technical | Train | 8 | nan | Czech | 68 | 68 | — |
| Technical | Train | 9 | nan | Czech | 67 | 67 | — |
| Technical | Train | 10 | nan | Polish | 0 | 49 | — |
| Technical | Train | 11 | nan | Ruthenian | 0 | 58 | — |
| Technical | Train | 12 | nan | Magyar | 0 | 67 | — |
| Technical | Train | 13 | nan | Croat-Serb | 0 | 68 | — |
| Technical | Train | 14 | nan | German | 30 | 66 | — |
| Technical | Train | 15 | nan | Croat-Serb | 0 | 57 | — |
| Technical | Train | 16 | nan | Croat-Serb | 0 | 52 | — |
| Infantry | Landesschützen | II. | Brixen; Trient | German | 0 | 55 | — |
| Infantry | Landesschützen | III. | Brixen; Trient | German | 0 | 59 | — |
| Cavalry | Landwehr-Ulan | 1 | nan | Ruthenian | 0 | 65 | — |
| Cavalry | Landwehr-Ulan | 2 | nan | Czech | 58 | 58 | — |
| Cavalry | Landwehr-Ulan | 3 | nan | Polish | 0 | 69 | — |
| Infantry | Landesschützen | I. | Salzburg; Innsbruck; Trient | German | 0 | 58 | — |
| Cavalry | Landwehr-Ulan | 4 | nan | Polish | 0 | 85 | — |
| Cavalry | Landwehr-Ulan | 5 | nan | German | 0 | 97 | — |
| Cavalry | Landwehr-Ulan | 6 | nan | German | 39 | 60 | — |
| Infantry | Landesschützen | nan | nan | German | 0 | 58 | — |
| Infantry | Landesschützen | nan | nan | Croat-Serb | 0 | 82 | — |
| Cavalry | Honvéd-Husar | 1 | nan | Magyar | 0 | 91 | — |
| Cavalry | Honvéd-Husar | 2 | nan | Magyar | 0 | 92 | — |
| Cavalry | Honvéd-Husar | 3 | nan | Magyar | 0 | 79 | — |
| Cavalry | Honvéd-Husar | 4 | nan | Magyar | 0 | 76 | — |
| Cavalry | Honvéd-Husar | 5 | nan | Magyar | 0 | 91 | — |
| Cavalry | Honvéd-Husar | 6 | nan | Magyar | 0 | 55 | — |
| Cavalry | Honvéd-Husar | 7 | nan | Magyar | 0 | 54 | — |
| Cavalry | Honvéd-Husar | 8 | nan | Magyar | 0 | 52 | — |
| Cavalry | Honvéd-Husar | 9 | nan | Magyar | 0 | 49 | — |
| Cavalry | Honvéd-Husar | 10 | nan | Croat-Serb | 0 | 97 | — |
| Artillery | Honvéd-Artillerie | 1 | nan | Magyar | 0 | 93 | — |
| Artillery | Honvéd-Artillerie | 2 | nan | Romanian | 0 | 69 | — |
| Artillery | Honvéd-Artillerie | 3 | nan | Magyar | 0 | 91 | — |
| Artillery | Honvéd-Artillerie | 4 | nan | Slovak | 0 | 51 | — |
| Artillery | Honvéd-Artillerie | 5 | nan | Magyar | 0 | 49 | — |
| Artillery | Honvéd-Artillerie | 6 | nan | Croat-Serb | 0 | 97 | — |
| Artillery | Honvéd-Artillerie | 7 | nan | Slovak | 0 | 51 | — |
| Artillery | Honvéd-Artillerie | 8 | nan | Magyar | 0 | 92 | — |
| Artillery | Landwehr-Feldkanonen | 13 | nan | German | 0 | 83 | — |
| Artillery | Landwehr-Feldkanonen | 21 | nan | Czech | 72 | 72 | — |
| Artillery | Landwehr-Feldkanonen | 22 | nan | German | 0 | 71 | — |
| Artillery | Landwehr-Feldkanonen | 26 | nan | German | 43 | 55 | — |
| Artillery | Landwehr-Feldkanonen | 43 | nan | Ruthenian | 0 | 55 | — |
| Artillery | Landwehr-Feldkanonen | 44 | nan | German | 39 | 60 | — |
| Artillery | Landwehr-Feldkanonen | 45 | nan | Ruthenian | 0 | 60 | — |
| Artillery | Landwehr-Feldkanonen | 46 | nan | Ruthenian | 0 | 49 | — |
| Artillery | Landwehr-Feldhaubitz | 13 | nan | German | 0 | 83 | — |
| Artillery | Landwehr-Feldhaubitz | 21 | nan | Czech | 72 | 72 | — |
| Artillery | Landwehr-Feldhaubitz | 22 | nan | German | 0 | 71 | — |
| Artillery | Landwehr-Feldhaubitz | 26 | nan | German | 43 | 55 | — |
| Artillery | Landwehr-Feldhaubitz | 43 | nan | Ruthenian | 0 | 55 | — |
| Artillery | Landwehr-Feldhaubitz | 44 | nan | German | 39 | 60 | — |
| Artillery | Landwehr-Feldhaubitz | 45 | nan | Ruthenian | 0 | 60 | — |
| Artillery | Landwehr-Feldhaubitz | 46 | nan | Ruthenian | 0 | 49 | — |
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.
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.
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/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_link_bridge.pyPipeline scripts
Applies ``czech_glosses.py`` to the curation skeleton and fills in the · source: utils/reference_tools/occupations/run_link_bridge.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
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 ; Cisleithanian districts (politické
okresy) ; Transleithanian districts (járások) ; Slovak municipalities (obce)
. Missing propagates: any function of a missing input is missing
() 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 |
|
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
— outside ⇒ :
| Variable | Data | Source | Window | 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
(so 6.12.19 → 1919, not 2019) — and, for the three partial-date columns only, the mirror
image iff
(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:
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):
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):
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
(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
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 ({p}_clean) and its within-system ordinal
or
, per
ranks/rank_ladders.md §A/§B/§B-gen. Progressions keep both
ends:
Column names below are written with the prefix standing in for the template
{p} used in the prose — i.e. denotes the column {p}_ord
(habsburg_rank_ord, legion_rank_ord, last_legion_rank_ord).
over the ranks listed in the cell; {p}_clean is the label attaining the max.
Non-ranks are dropped to : 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 (,
); 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:
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 , Legion poručík — the same
OF-1 grade). The bridge is the name-anchored common grade
(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, …):
Rank reset (ranks.add_rank_reset; relative rank deprivation on Legion
enrolment):
Negative ⇒ demotion on entering the Legion. 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:
7,696 records (5.8%). Lead the headline reset on
(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
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 .
5 · Unit identity and theatre of service
Unit (units.add_unit_columns). The raw field is label ( formation ). Let
be the label with the parenthetical stripped (regiment) and 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
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 .
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:
- keyword regex on the enrolment-unit string;
- regiment-mate majority vote: for regiment (unit text before the parenthetical), — inline maths must stay on one line;
- 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); - 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
6 · Demographics
| Variable | Data | Source | Rule |
|---|---|---|---|
education (label), education_ord |
L | Vzdělání |
Highest institution attended on the 1869 Austrian ladder; ordinal : 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: over the institutions 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í |
— 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,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í |
, 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 |
; 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 |
— 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
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 is whatever survives.
| Variable | Data | Source | Rule |
|---|---|---|---|
record_source |
L | Zdroj informací |
passthrough (verbatim; 625 distinct values) |
source_spis |
L | same | — the VÚA-VHA service file. 127,222 (95.7%) |
source_karta |
L | same | — the personal card, tested after the service-file prefix is removed. 102,168 (76.8%); both documents 97,075 |
source_extra |
L | same | — a source cited beyond the two personnel documents. 5,823 (4.4%) |
source_extra_ref |
L | same | verbatim, where ; 489 distinct (published okres volumes, regimental memorial books, family archives, museum fonds) |
Missing stays missing. Where is (431 records) all four columns are , never — 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_uncertain = ? present; every dummy 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 |
— 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:
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); = 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, …); 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 , Transleithanian
on .
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:
= rapidfuzz token_set_ratio on ,
likewise on the given names. A candidate pair is a confirmed overlap iff
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 |
|
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; 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, = 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 ⇒ ):
(lang_czech = the census bloc Böhmisch–Mährisch–Slowakisch; lang_total =
Umgangssprache total, i.e. present citizens);
(present population summed over settlement-size classes);
Court-level source files (934 units) are aggregated to the okres before any ratio: (counts only).
Transleithanian járás panel (census_hu_jaras_1910, = járás/town)
Mother-tongue shares (built in census_hu from the ETN table; base = present
population):
and identically magyar_share, german_share, ruthen_share (numerators
magyar/német/ruthén anyanyelv). Religion shares (add_shares_hu):
Slovak obec crosswalk (settlements_hu_1910, = 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):
likewise magyar_share, german_share, rusyn_share;
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 |
|
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 |
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. — 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 (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 = 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
| dataset | final_name | source_name | description |
|---|---|---|---|
| legion | legion_id | ID | Stable csol.cz record id (primary key) |
| legion | surname | surname | Cleaned surname |
| legion | given_name | given | Cleaned given name |
| legion | name_uncertain | name_uncertain | True if the raw name carried an uncertainty mark (?) |
| legion | birth_date | birth_date | Parsed date of birth (NaT if unresolved) |
| legion | birth_year | birth_year | Year of birth (from birth_date) |
| legion | birth_year_only | birth_year_only | True if only a year (not full date) was available |
| legion | birth_municipality | Obec narození | Raw municipality of birth (obec), as recorded — see birth_municipality_cs/_de for standardised names |
| legion | birth_okres | Okres narození | Raw district of birth, as recorded — see birth_okres_cs/_de/_id (census key) and birth_court_* (soudní okres) |
| legion | home_municipality | Domovská obec | Raw home municipality (domicile), as recorded — see home_municipality_cs/_de for standardised names |
| legion | home_okres | Domovský okres | Raw home district (domicile), as recorded — see home_okres_cs/_de/_id and home_court_* (soudní okres) |
| legion | religion | Vyznání | Declared religion (Vyznání) |
| legion | religion_en | religion_en | English gloss of religion (řím.kat.→Roman Catholic, pravosl.→Orthodox, …); <NA> for the rare unmapped tail (from religion.py) |
| legion | education | education | Highest school ATTENDED, on the 1869 Austrian ladder: No schooling / Elementary / Burgher school / Vocational secondary / Upper secondary / Tertiary (from education.py 2.0.0) |
| legion | education_ord | educ_ord | The 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 |
| legion | education_track | educ_track | Branch 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 |
| legion | education_apprentice | educ_apprentice | Attended a continuation school (pokračovací š. / gewerbliche Fortbildungsschule), i.e. the apprentice track |
| legion | education_top | educ_top | Canonical 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 |
| legion | education_top_imputed | educ_top_imputed | True 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) |
| legion | education_institutions | educ_institutions | Every institution named in the record, pipe-joined in ladder order (audit column) |
| legion | education_years_elementary | educ_years_elementary | Years stated at the Volksschule (obecná škola) |
| legion | education_years_burgher | educ_years_burgher | Years stated at the Bürgerschule (měšťanská škola) |
| legion | education_years_continuation | educ_years_continuation | Years 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 |
| legion | education_years_secondary | educ_years_secondary | Years stated at the highest post-compulsory institution. Semesters are NOT counted here — see education_semesters |
| legion | education_semesters | educ_semesters | Semesters 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) |
| legion | education_complete | educ_complete | Finished 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 |
| legion | occupation | Zaměstnání | Occupation at enrolment (Zaměstnání) |
| legion | occupation_interwar | Povolání v letech 1918 - 1938 | Occupation 1918–1938 |
| legion | occupation_hisco | occupation_hisco | HISCO occupational code at enrolment, 5-digit micro group (from occupations.py) |
| legion | occupation_hisco_major | occupation_hisco_major | HISCO major group, 1 digit (0/1 professional … 9 unskilled) |
| legion | occupation_en | occupation_en | English HISCO label of the occupation at enrolment |
| legion | occupation_hisclass | occupation_hisclass | HISCLASS social class, 1 (higher managers) – 12 (unskilled farm workers), 13 = unskilled, unspecified |
| legion | occupation_hisclass_label | occupation_hisclass_label | Readable HISCLASS name |
| legion | occupation_hisclass_5 | occupation_hisclass_5 | HISCLASS collapsed to 5 classes: 1 elite, 2 lower middle, 3 skilled, 4 farmers, 5 unskilled |
| legion | occupation_socpo | occupation_socpo | SOCPO social-power level: 1 unskilled, 2 semi-skilled, 3 skilled, 41 farmers, 42 supervisors, 5 elite |
| legion | occupation_status | occupation_status | HISCO 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 |
| legion | occupation_relation | occupation_relation | HISCO relation modifier (21 former/retired, …); -9 = none. Any value here means HISCLASS is deliberately absent |
| legion | occupation_match | occupation_match | How the raw string was matched: exact / dual / head / prefix (<NA> if uncoded) — see OCCUPATION_CODING.md |
| legion | occupation_qualifier | occupation_qualifier | The 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) |
| legion | occupation_career_stage | occupation_career_stage | Position 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 |
| legion | occupation_career_stage_ord | occupation_career_stage_ord | occupation_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 |
| legion | occupation_hisco_2 | occupation_hisco_2 | HISCO code of the *second* occupation where two were recorded (occupation_match='dual') |
| legion | occupation_en_2 | occupation_en_2 | English HISCO label of the second occupation |
| legion | occupation_hisclass_2 | occupation_hisclass_2 | HISCLASS of the second occupation |
| legion | occupation_hisclass_high | occupation_hisclass_high | The higher-status of the two HISCLASS values (numerically the lower); equals occupation_hisclass when only one occupation was recorded |
| legion | occupation_interwar_hisco | occupation_interwar_hisco | HISCO code of the 1918–1938 occupation |
| legion | occupation_interwar_en | occupation_interwar_en | English HISCO label of the 1918–1938 occupation |
| legion | occupation_interwar_hisclass | occupation_interwar_hisclass | HISCLASS of the 1918–1938 occupation |
| legion | occupation_interwar_hisclass_5 | occupation_interwar_hisclass_5 | HISCLASS_5 of the 1918–1938 occupation — the same 5-class scale as occupation_hisclass_5, so the two fields are comparable |
| legion | occupation_interwar_qualifier | occupation_interwar_qualifier | occupation_qualifier for the 1918–1938 occupation |
| legion | occupation_interwar_career_stage | occupation_interwar_career_stage | occupation_career_stage for the 1918–1938 occupation |
| legion | occupation_interwar_career_stage_ord | occupation_interwar_career_stage_ord | occupation_career_stage_ord for the 1918–1938 occupation |
| legion | occupation_interwar_match | occupation_interwar_match | How the 1918–1938 string was matched: exact / head / prefix (<NA> if uncoded) — lets the exact-only robustness check run on this field too |
| legion | habsburg_unit_peacetime | Prezenční služba u rakousko-uherského útvaru | Habsburg peacetime unit (Prezenční služba) |
| legion | habsburg_unit_capture | Rakousko-uherský útvar v době zajetí | Habsburg unit at time of capture |
| legion | habsburg_unit_capture_2 | Rakousko-uherský útvar v době zajetí_2 | Secondary Habsburg unit at capture |
| legion | habsburg_rank | Hodnost v době zajetí | Habsburg rank at capture (raw string) |
| legion | habsburg_rank_clean | habsburg_rank_clean | Standardised Habsburg rank — canonical Czech label (highest if a progression) |
| legion | habsburg_rank_en | habsburg_rank_en | English gloss of the standardised Habsburg rank (German k.u.k. term in parentheses) |
| legion | habsburg_rank_ord | habsburg_rank_ord | Habsburg rank ordinal 1–20 (k.u.k. ladder; highest reached; <NA> if unresolved) |
| legion | habsburg_rank_ord_low | habsburg_rank_ord_low | Lowest ordinal among listed ranks (= ord for singletons; preserves progressions) |
| legion | habsburg_rank_jd | habsburg_rank_jd | True if a one-year-volunteer (jednoroční dobrovolník / Einjährig-Freiwilliger) marker was present; <NA> where the rank cell is blank |
| legion | habsburg_rank_branch | habsburg_rank_branch | Arm of service inferred from the rank term (artillery/cavalry/jäger/engineers/infantry/rifles; sparse) |
| legion | habsburg_rank_tier | habsburg_rank_tier | Coarse rank tier (Mannschaft / NCO / Aspirant / Officer) |
| legion | habsburg_rank_tier_en | habsburg_rank_tier_en | English gloss of the Habsburg rank tier (Enlisted / NCO / Officer …) |
| legion | habsburg_rank_common | habsburg_rank_common | System-blind ALT-1 common grade of the Habsburg capture rank (subtractable across systems; rank_ladders.md §G) |
| legion | habsburg_rank_band | habsburg_rank_band | Coarse NATO OF/OR band of the Habsburg capture rank (APersP-01; robustness axis) |
| legion | capture_date | capture_date | Parsed 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 |
| legion | capture_date_precision | capture_date_precision | How exact capture_date is: day / month / year; <NA> where no date parsed (dates.py 1.2.0) |
| legion | capture_place | Misto zajetí | Place of capture |
| legion | application_date | Datum podání přihlášky do legií | Date of application to the Legions |
| legion | application_place | Misto podání přihlášky do legií | Place of application to the Legions |
| legion | enroll_date | enroll_date | Parsed 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 |
| legion | enroll_date_precision | enroll_date_precision | How exact enroll_date is: day / month / year; <NA> where no date parsed (dates.py 1.2.0) |
| legion | legion_unit | Jednotka v době zařazení | Legion unit at enrolment (raw Jednotka string) |
| legion | regiment | regiment | Clean regiment label, display only — shared across armies (from units.py) |
| legion | regiment_en | regiment_en | English rendering of the regiment label (6.stř.pl.→6th Rifle Regiment); unmapped/foreign units pass through verbatim (from units.py translate_unit) |
| legion | legion_army | legion_army | Army/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) |
| legion | unit_key | unit_key | Unit identity for analysis: 'regiment @ army'. Use instead of regiment for unit FE, clustering and size denominators |
| legion | unit_key_en | unit_key_en | English form of unit_key: 'regiment_en @ army' |
| legion | unit_type | unit_type | Arm / unit type (from units.py) |
| legion | legion_rank | Hodnost v době zařazení | Legion rank at enrolment (raw string) |
| legion | legion_rank_clean | legion_rank_clean | Standardised Legion enrolment rank — canonical Czech label (highest if a progression) |
| legion | legion_rank_en | legion_rank_en | English gloss of the standardised Legion enrolment rank |
| legion | legion_rank_ord | legion_rank_ord | Legion enrolment-rank ordinal 1–14 (Legion ladder §B; highest reached; <NA> if unresolved) |
| legion | legion_rank_ord_low | legion_rank_ord_low | Lowest ordinal among listed enrolment ranks (= ord for singletons; preserves progressions) |
| legion | legion_rank_jd | legion_rank_jd | True if a one-year-volunteer (jednoroční dobrovolník) marker was present on the enrolment rank; <NA> where the rank cell is blank |
| legion | legion_rank_branch | legion_rank_branch | Arm of service inferred from the enrolment-rank term (artillery/cavalry/infantry/rifles/engineers/labour; sparse) |
| legion | legion_rank_tier | legion_rank_tier | Coarse Legion rank tier (Mužstvo / Poddůstojník / Aspirant / Důstojník / Štábní důstojník) |
| legion | legion_rank_tier_en | legion_rank_tier_en | English gloss of the Legion rank tier (Enlisted / NCO / Officer candidate / Officer / Staff officer / General) |
| legion | legion_rank_common | legion_rank_common | System-blind ALT-1 common grade of the Legion enrolment rank (subtractable across systems; rank_ladders.md §G) |
| legion | legion_rank_band | legion_rank_band | Coarse NATO OF/OR band of the Legion enrolment rank (APersP-01; robustness axis) |
| legion | rank_reset | rank_reset | Relative 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. |
| legion | rank_reset_band | rank_reset_band | Coarse-band robustness variant of rank_reset on the NATO OF/OR index (all subalterns share OF-1, so subaltern↔subaltern = 0). |
| legion | legion_noncombat | legion_noncombat | True 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). |
| legion | theater | theater | Legion: 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 |
| legion | exit_codes | exit_codes_str | Exit codes as ';'-joined string (D=deserted, B=KIA, …) |
| legion | exit_B | exit_B | True 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 |
| legion | exit_ČSA | exit_ČSA | True 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 |
| legion | exit_D | exit_D | True if the exit codes include D (Deserted). False where they do not, including records with no exit code at all, which are censored |
| legion | exit_DM | exit_DM | True if the exit codes include DM (Demobilised). False where they do not, including records with no exit code at all, which are censored |
| legion | exit_N | exit_N | True if the exit codes include N (Missing). False where they do not, including records with no exit code at all, which are censored |
| legion | exit_NS | exit_NS | True 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 |
| legion | exit_P | exit_P | True if the exit codes include P (Executed). False where they do not, including records with no exit code at all, which are censored |
| legion | exit_S | exit_S | True if the exit codes include S (Died). False where they do not, including records with no exit code at all, which are censored |
| legion | exit_V | exit_V | True if the exit codes include V (Returned home). False where they do not, including records with no exit code at all, which are censored |
| legion | exit_X | exit_X | True if the exit codes include X (See note). False where they do not, including records with no exit code at all, which are censored |
| legion | exit_Z | exit_Z | True 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 |
| legion | deserted | deserted | True 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 |
| legion | exit_date | exit_date | Date 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 |
| legion | exit_date_precision | exit_date_precision | How exact exit_date is: day / month / year; <NA> where no date parsed (exit_codes.py 1.2.0) |
| legion | last_legion_unit | Poslední útvar v legiích | Last Legion unit |
| legion | last_legion_unit_en | last_legion_unit_en | English rendering of the last Legion unit (from units.py translate_unit) |
| legion | last_legion_rank | Poslední hodnost v legiích | Last Legion rank (raw string) |
| legion | last_legion_rank_clean | last_legion_rank_clean | Standardised last Legion rank — canonical Czech label (highest if a progression) |
| legion | last_legion_rank_en | last_legion_rank_en | English gloss of the standardised last Legion rank |
| legion | last_legion_rank_ord | last_legion_rank_ord | Last Legion-rank ordinal 1–14 (Legion ladder §B; highest reached; <NA> if unresolved) |
| legion | last_legion_rank_ord_low | last_legion_rank_ord_low | Lowest ordinal among listed last-ranks (= ord for singletons) |
| legion | last_legion_rank_jd | last_legion_rank_jd | True if a one-year-volunteer marker was present on the last rank; <NA> where the rank cell is blank |
| legion | last_legion_rank_branch | last_legion_rank_branch | Arm of service inferred from the last-rank term (sparse) |
| legion | last_legion_rank_tier | last_legion_rank_tier | Coarse Legion rank tier of the last rank (Mužstvo … Štábní důstojník) |
| legion | last_legion_rank_tier_en | last_legion_rank_tier_en | English gloss of the last Legion rank tier (Enlisted … General) |
| legion | last_legion_rank_common | last_legion_rank_common | System-blind ALT-1 common grade of the last Legion rank (rank_ladders.md §G) |
| legion | last_legion_rank_band | last_legion_rank_band | Coarse NATO OF/OR band of the last Legion rank (APersP-01; robustness axis) |
| legion | death_date | death_date | Parsed date of death, 1914–1923 only (war-period mortality). For legionnaires the post-1923 deaths of veterans are NaT here — see death_date_raw |
| legion | death_year_only | death_year_only | True if only a year was available for death |
| legion | death_date_raw | Datum úmrtí | Legionnaires: the raw Datum úmrtí string, kept because 8,034 of 12,480 recorded deaths fall after 1923 and outside death_date |
| legion | death_cause_raw | Příčina úmrtí | Raw cause-of-death string (legionnaire, unparsed) |
| legion | death_place | Místo úmrtí | Place of death |
| legion | burial_place | Místo pohřbení | Place of burial |
| legion | legion_organization | Legionářska organizace | Veteran legion organisation membership |
| legion | notes | Poznámka | Free-text note (Poznámka) |
| legion | record_source | Zdroj 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 |
| legion | source_spis | source_spis | True if the record cites a legionářský poslužný spis (VÚA-VHA service file). <NA> where no source is recorded |
| legion | source_karta | source_karta | True if the record cites an osobní karta legionáře (VÚA-VHA personal card). <NA> where no source is recorded |
| legion | source_extra | source_extra | True 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 |
| legion | source_extra_ref | source_extra_ref | The extra citation verbatim, <NA> where there is none. ~489 distinct values |
| legion | pol_* | Politická příslušnost | pol_* — 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 |
| legion | in_other_dataset | utils.linkage (notebook 02 §4) | True if this person was matched in the other dataset (confirmed overlap; see match_confidence) |
| legion | match_id | utils.linkage (notebook 02 §4) | Matched id in the other dataset (vua_id ↔ legion_id) |
| legion | match_confidence | utils.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) |
| legion | dup_group | utils.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 |
| legion | birth_municipality_cs | birth_municipality / birth_okres (derived) | Standardised modern Czech birth municipality (Historický lexikon obcí) |
| legion | birth_municipality_de | birth_municipality / birth_okres (derived) | Standardised German birth municipality (1910 exonym; Wikidata/lexikon; NA where none existed) |
| legion | birth_okres_id | birth_municipality / birth_okres (derived) | 1910 political-okres id (region·1000+district) of birth — administrative unit |
| legion | birth_okres_match | birth_municipality / birth_okres (derived) | How birth_okres_id resolved (okres/court/fuzzy/alias/settlement/muni) |
| legion | birth_okres_twin | birth_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 |
| legion | birth_court_snapped | birth_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 |
| legion | birth_okres_prague | birth_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 |
| legion | birth_census_okres_id | birth_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) |
| legion | birth_okres_cs | birth_municipality / birth_okres (derived) | 1910 political-okres Czech name of birth (MPIDR backbone) |
| legion | birth_okres_de | birth_municipality / birth_okres (derived) | 1910 political-okres German name of birth (MPIDR backbone) |
| legion | birth_court_id | birth_municipality / birth_okres (derived) | 1910 court district (soudní okres/Gerichtsbezirk) MPIDR id of birth, via settlement spatial join |
| legion | birth_court_cs | birth_municipality / birth_okres (derived) | Court-district (soudní okres) Czech name of birth |
| legion | birth_court_de | birth_municipality / birth_okres (derived) | Court-district (Gerichtsbezirk) German name of birth |
| legion | birth_land | birth_municipality / birth_okres (derived) | Crown land/country of birth (Czech lands/Slovakia/Austria/Galicia/…) — positive district classification |
| legion | birth_lat | birth_municipality / birth_okres (derived) | Latitude of birth settlement (WGS84; Wikidata) |
| legion | birth_lon | birth_municipality / birth_okres (derived) | Longitude of birth settlement (WGS84; Wikidata) |
| legion | birth_qid | birth_municipality / birth_okres (derived) | Wikidata QID of birth settlement |
| legion | birth_jaras_id | birth_okres (derived, Transleithanian) | 1910 járás (district) unit id of birth — Upper-Hungary census key (GISta Hungarorum) |
| legion | birth_jaras_match | birth_okres (derived, Transleithanian) | How birth_jaras_id resolved (jaras/town/megye/fuzzy/settlement) |
| legion | birth_jaras_twin | birth_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 |
| legion | birth_jaras_sk | birth_okres (derived, Transleithanian) | Slovak/Rusyn járás name of birth |
| legion | birth_jaras_hu | birth_okres (derived, Transleithanian) | Magyar járás/seat name of birth |
| legion | birth_megye_id | birth_okres (derived, Transleithanian) | 1910 vármegye (county) id of birth |
| legion | birth_megye_sk | birth_okres (derived, Transleithanian) | Slovak/Rusyn county name of birth |
| legion | birth_megye_hu | birth_okres (derived, Transleithanian) | Magyar county name of birth |
| legion | birth_obec_sk | birth_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 |
| legion | home_municipality_cs | home_municipality / home_okres (derived) | Standardised modern Czech home (domicile) municipality (Historický lexikon obcí) |
| legion | home_municipality_de | home_municipality / home_okres (derived) | Standardised German home (domicile) municipality (1910 exonym; Wikidata/lexikon; NA where none existed) |
| legion | home_okres_id | home_municipality / home_okres (derived) | 1910 political-okres id (region·1000+district) of home (domicile) — administrative unit |
| legion | home_okres_match | home_municipality / home_okres (derived) | How home (domicile)_okres_id resolved (okres/court/fuzzy/alias/settlement/muni) |
| legion | home_okres_twin | home_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 |
| legion | home_court_snapped | home_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 |
| legion | home_okres_prague | home_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 |
| legion | home_census_okres_id | home_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) |
| legion | home_okres_cs | home_municipality / home_okres (derived) | 1910 political-okres Czech name of home (domicile) (MPIDR backbone) |
| legion | home_okres_de | home_municipality / home_okres (derived) | 1910 political-okres German name of home (domicile) (MPIDR backbone) |
| legion | home_court_id | home_municipality / home_okres (derived) | 1910 court district (soudní okres/Gerichtsbezirk) MPIDR id of home (domicile), via settlement spatial join |
| legion | home_court_cs | home_municipality / home_okres (derived) | Court-district (soudní okres) Czech name of home (domicile) |
| legion | home_court_de | home_municipality / home_okres (derived) | Court-district (Gerichtsbezirk) German name of home (domicile) |
| legion | home_land | home_municipality / home_okres (derived) | Crown land/country of home (domicile) (Czech lands/Slovakia/Austria/Galicia/…) — positive district classification |
| legion | home_lat | home_municipality / home_okres (derived) | Latitude of home (domicile) settlement (WGS84; Wikidata) |
| legion | home_lon | home_municipality / home_okres (derived) | Longitude of home (domicile) settlement (WGS84; Wikidata) |
| legion | home_qid | home_municipality / home_okres (derived) | Wikidata QID of home (domicile) settlement |
| legion | home_jaras_id | home_okres (derived, Transleithanian) | 1910 járás (district) unit id of home (domicile) — Upper-Hungary census key (GISta Hungarorum) |
| legion | home_jaras_match | home_okres (derived, Transleithanian) | How home (domicile)_jaras_id resolved (jaras/town/megye/fuzzy/settlement) |
| legion | home_jaras_twin | home_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 |
| legion | home_jaras_sk | home_okres (derived, Transleithanian) | Slovak/Rusyn járás name of home (domicile) |
| legion | home_jaras_hu | home_okres (derived, Transleithanian) | Magyar járás/seat name of home (domicile) |
| legion | home_megye_id | home_okres (derived, Transleithanian) | 1910 vármegye (county) id of home (domicile) |
| legion | home_megye_sk | home_okres (derived, Transleithanian) | Slovak/Rusyn county name of home (domicile) |
| legion | home_megye_hu | home_okres (derived, Transleithanian) | Magyar county name of home (domicile) |
| legion | home_obec_sk | home_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 |
| habsburg | vua_id | vua_id | Stable VÚA record id (primary key) |
| habsburg | surname | surname | Cleaned surname |
| habsburg | given_name | given | Cleaned given name |
| habsburg | name_uncertain | name_uncertain | True if the raw name carried an uncertainty mark (?) |
| habsburg | birth_date | birth_date | Parsed date of birth (NaT if unresolved) |
| habsburg | birth_year | birth_year | Year of birth (from birth_date) |
| habsburg | birth_year_only | birth_year_only | True if only a year (not full date) was available |
| habsburg | birth_municipality | birth_municipality | Raw municipality of birth (obec), as recorded — see birth_municipality_cs/_de for standardised names |
| habsburg | birth_okres | birth_okres | Raw district of birth, as recorded — see birth_okres_cs/_de/_id (census key) and birth_court_* (soudní okres) |
| habsburg | birth_german | birth_german | German exonym of birthplace, if present |
| habsburg | birth_land | birth_land | Crown land of birth (Czech lands / Slovakia / …) |
| habsburg | birth_cisleithanian | birth_cisleithanian | True 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 |
| habsburg | birth_loc_uncertain | birth_loc_uncertain | True if birthplace parse was uncertain |
| habsburg | home_municipality | home_municipality | Raw home municipality (domicile), as recorded — see home_municipality_cs/_de for standardised names |
| habsburg | home_okres | home_okres | Raw home district (domicile), as recorded — see home_okres_cs/_de/_id and home_court_* (soudní okres) |
| habsburg | home_land | home_land | Home crown land |
| habsburg | home_cisleithanian | home_cisleithanian | As birth_cisleithanian, for the home (domicile) place — derived from the corrected home_land |
| habsburg | death_date | death_date | Parsed date of death, 1914–1923 only (war-period mortality). For legionnaires the post-1923 deaths of veterans are NaT here — see death_date_raw |
| habsburg | death_year | death_year | Year of death (from death_date) |
| habsburg | death_year_only | death_year_only | True if only a year was available for death |
| habsburg | age_at_death | age_at_death | death_year − birth_year |
| habsburg | death_place | Místo úmrtí | Place of death |
| habsburg | burial_place | Místo pohřbení | Place of burial |
| habsburg | cause | cause | Primary cause-of-death code (KIA/DIS/MIS/EXE/DRO/SUI/CAP) |
| habsburg | cause_label | cause_label | English label for primary cause |
| habsburg | cause_uncertain | cause_uncertain | True if the raw cause carried a ?; <NA> where no cause was recorded |
| habsburg | cause_KIA | cause_KIA | True 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) |
| habsburg | cause_DIS | cause_DIS | True 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) |
| habsburg | cause_MIS | cause_MIS | True if the cause field mentions missing; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0) |
| habsburg | cause_EXE | cause_EXE | True if the cause field mentions execution; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0) |
| habsburg | cause_DRO | cause_DRO | True if the cause field mentions drowning; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0) |
| habsburg | cause_SUI | cause_SUI | True if the cause field mentions suicide; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0) |
| habsburg | cause_CAP | cause_CAP | True if the cause field mentions capture; False if it records another cause; <NA> where no cause was recorded (habsburg.py 1.2.0) |
| habsburg | theater | theatre | Legion: 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 |
| habsburg | in_captivity | in_captivity | True if the battlefield field records death in captivity (v zajetí); <NA> where Bojiště is blank |
| habsburg | ah_unit_primary | ah_unit_primary | Primary Austro-Hungarian unit (raw) |
| habsburg | ah_unit_type | ah_unit_type | Habsburg arm/branch (Infantry / Artillery / …) |
| habsburg | ah_unit_org | ah_unit_org | Habsburg organisation (Common army / Landwehr / Honvéd / Landsturm) |
| habsburg | ah_regiment_no | ah_regiment_no | Habsburg regiment number |
| habsburg | ah_n_units | ah_n_units | Count of ';'-joined Habsburg units |
| habsburg | note_legion | note_legion | True 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) |
| habsburg | note_legion_ref | note_legion_ref | The Legion phrase matched in the note (e.g. 'Kancelář legií', 'KLEG', 'legionář') |
| habsburg | note_register_ref | note_register_ref | The interwar Czechoslovak army unit whose death register lists the man ('náhradní prapor čs. pěšího pluku 33'), verbatim — archival provenance, NOT Legion service |
| habsburg | note_declared_dead | note_declared_dead | True if legally declared dead |
| habsburg | note_dead_ref | note_dead_ref | File reference for the death declaration |
| habsburg | note_rank | note_rank | Rank recovered from the note |
| habsburg | note_disease | note_disease | Disease detail recovered from the note |
| habsburg | notes | Poznámka | Free-text note (Poznámka) |
| habsburg | in_other_dataset | utils.linkage (notebook 02 §4) | True if this person was matched in the other dataset (confirmed overlap; see match_confidence) |
| habsburg | match_id | utils.linkage (notebook 02 §4) | Matched id in the other dataset (vua_id ↔ legion_id) |
| habsburg | match_confidence | utils.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) |
| habsburg | dup_group | utils.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 |
| habsburg | birth_municipality_cs | birth_municipality / birth_okres (derived) | Standardised modern Czech birth municipality (Historický lexikon obcí) |
| habsburg | birth_municipality_de | birth_municipality / birth_okres (derived) | Standardised German birth municipality (1910 exonym; Wikidata/lexikon; NA where none existed) |
| habsburg | birth_okres_id | birth_municipality / birth_okres (derived) | 1910 political-okres id (region·1000+district) of birth — administrative unit |
| habsburg | birth_okres_match | birth_municipality / birth_okres (derived) | How birth_okres_id resolved (okres/court/fuzzy/alias/settlement/muni) |
| habsburg | birth_okres_twin | birth_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 |
| habsburg | birth_court_snapped | birth_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 |
| habsburg | birth_okres_prague | birth_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 |
| habsburg | birth_census_okres_id | birth_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) |
| habsburg | birth_okres_cs | birth_municipality / birth_okres (derived) | 1910 political-okres Czech name of birth (MPIDR backbone) |
| habsburg | birth_okres_de | birth_municipality / birth_okres (derived) | 1910 political-okres German name of birth (MPIDR backbone) |
| habsburg | birth_court_id | birth_municipality / birth_okres (derived) | 1910 court district (soudní okres/Gerichtsbezirk) MPIDR id of birth, via settlement spatial join |
| habsburg | birth_court_cs | birth_municipality / birth_okres (derived) | Court-district (soudní okres) Czech name of birth |
| habsburg | birth_court_de | birth_municipality / birth_okres (derived) | Court-district (Gerichtsbezirk) German name of birth |
| habsburg | birth_lat | birth_municipality / birth_okres (derived) | Latitude of birth settlement (WGS84; Wikidata) |
| habsburg | birth_lon | birth_municipality / birth_okres (derived) | Longitude of birth settlement (WGS84; Wikidata) |
| habsburg | birth_qid | birth_municipality / birth_okres (derived) | Wikidata QID of birth settlement |
| habsburg | birth_jaras_id | birth_okres (derived, Transleithanian) | 1910 járás (district) unit id of birth — Upper-Hungary census key (GISta Hungarorum) |
| habsburg | birth_jaras_match | birth_okres (derived, Transleithanian) | How birth_jaras_id resolved (jaras/town/megye/fuzzy/settlement) |
| habsburg | birth_jaras_twin | birth_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 |
| habsburg | birth_jaras_sk | birth_okres (derived, Transleithanian) | Slovak/Rusyn járás name of birth |
| habsburg | birth_jaras_hu | birth_okres (derived, Transleithanian) | Magyar járás/seat name of birth |
| habsburg | birth_megye_id | birth_okres (derived, Transleithanian) | 1910 vármegye (county) id of birth |
| habsburg | birth_megye_sk | birth_okres (derived, Transleithanian) | Slovak/Rusyn county name of birth |
| habsburg | birth_megye_hu | birth_okres (derived, Transleithanian) | Magyar county name of birth |
| habsburg | birth_obec_sk | birth_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 |
| habsburg | home_municipality_cs | home_municipality / home_okres (derived) | Standardised modern Czech home (domicile) municipality (Historický lexikon obcí) |
| habsburg | home_municipality_de | home_municipality / home_okres (derived) | Standardised German home (domicile) municipality (1910 exonym; Wikidata/lexikon; NA where none existed) |
| habsburg | home_okres_id | home_municipality / home_okres (derived) | 1910 political-okres id (region·1000+district) of home (domicile) — administrative unit |
| habsburg | home_okres_match | home_municipality / home_okres (derived) | How home (domicile)_okres_id resolved (okres/court/fuzzy/alias/settlement/muni) |
| habsburg | home_okres_twin | home_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 |
| habsburg | home_court_snapped | home_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 |
| habsburg | home_okres_prague | home_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 |
| habsburg | home_census_okres_id | home_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) |
| habsburg | home_okres_cs | home_municipality / home_okres (derived) | 1910 political-okres Czech name of home (domicile) (MPIDR backbone) |
| habsburg | home_okres_de | home_municipality / home_okres (derived) | 1910 political-okres German name of home (domicile) (MPIDR backbone) |
| habsburg | home_court_id | home_municipality / home_okres (derived) | 1910 court district (soudní okres/Gerichtsbezirk) MPIDR id of home (domicile), via settlement spatial join |
| habsburg | home_court_cs | home_municipality / home_okres (derived) | Court-district (soudní okres) Czech name of home (domicile) |
| habsburg | home_court_de | home_municipality / home_okres (derived) | Court-district (Gerichtsbezirk) German name of home (domicile) |
| habsburg | home_lat | home_municipality / home_okres (derived) | Latitude of home (domicile) settlement (WGS84; Wikidata) |
| habsburg | home_lon | home_municipality / home_okres (derived) | Longitude of home (domicile) settlement (WGS84; Wikidata) |
| habsburg | home_qid | home_municipality / home_okres (derived) | Wikidata QID of home (domicile) settlement |
| habsburg | home_jaras_id | home_okres (derived, Transleithanian) | 1910 járás (district) unit id of home (domicile) — Upper-Hungary census key (GISta Hungarorum) |
| habsburg | home_jaras_match | home_okres (derived, Transleithanian) | How home (domicile)_jaras_id resolved (jaras/town/megye/fuzzy/settlement) |
| habsburg | home_jaras_twin | home_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 |
| habsburg | home_jaras_sk | home_okres (derived, Transleithanian) | Slovak/Rusyn járás name of home (domicile) |
| habsburg | home_jaras_hu | home_okres (derived, Transleithanian) | Magyar járás/seat name of home (domicile) |
| habsburg | home_megye_id | home_okres (derived, Transleithanian) | 1910 vármegye (county) id of home (domicile) |
| habsburg | home_megye_sk | home_okres (derived, Transleithanian) | Slovak/Rusyn county name of home (domicile) |
| habsburg | home_megye_hu | home_okres (derived, Transleithanian) | Magyar county name of home (domicile) |
| habsburg | home_obec_sk | home_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 |
| settlements | cs_name | lexikon headword | Settlement name, modern Czech (register key; + curated extras absent from the register, e.g. Praha) |
| settlements | name_1910 | pod názvem | Settlement name as used in 1910 (German exonym where the place was later renamed; else older Czech spelling; blank if unchanged) |
| settlements | okres_1910 | lexikon | 1910 political okres of the settlement (period name) |
| settlements | okres_id | records_geo | 1910 okres id (= districts okres_id; join key to census) |
| settlements | okres_match | records_geo | How okres_1910 resolved: okres / court / fuzzy |
| settlements | land | districts | Crown land (Böhmen / Mähren / Schlesien) |
| settlements | status_1910 | lexikon | 1910 status: obec (commune) / osada (hamlet) / cast (part-of) |
| settlements | parent_1910 | lexikon | Parent commune in 1910 where the settlement was dependent (osada/cast) |
| settlements | okres_all | lexikon | Pipe-joined okres timeline across all census years (used to recover post-1910 okres labels) |
| settlements | wd_qid | Wikidata | Wikidata item id (Q...) of the settlement, where geocoded |
| settlements | lat / lon | Wikidata P625 | Coordinates (WGS84); blank where not geocoded (~40% coverage, obce-level). okres_centroids_1910.csv = okres-mean lat/lon |
| districts | land | MPIDR LAND | Crown land (Böhmen / Mähren / Schlesien) |
| districts | okres_code | REGION-DISTRICT | Political-okres id (MPIDR region-district code) |
| districts | okres_de | MPIDR | Political okres (politický okres) seat name, German |
| districts | okres_cs | curated | Political okres name, canonical 1910 Czech (census-merge key) |
| districts | okres_cs_modern | curated | Political okres name today, if the seat was renamed (else blank) |
| districts | court_id | MPIDR ID_1 | Court-district (Gerichtsbezirk) unique id |
| districts | mpidr_oid | MPIDR OID_ | MPIDR polygon id (link to the .shp geometry) |
| districts | gerichtsbezirk_de | MPIDR NAME | Court district (soudní okres / Bezirksgericht), German |
| districts | gerichtsbezirk_cs | curated | Court district name, canonical 1910 Czech |
| districts | gerichtsbezirk_cs_modern | curated | Court district name today, if renamed (else blank) |
| districts | is_okres_seat | derived | True if this court district is the political-okres seat (the court whose Czech name equals the okres name, ignoring a '(město)' marker) |
| crosswalk | okres_id | districts | 1910 political-okres id (okres_census_crosswalk.csv) |
| crosswalk | census_okres_id | derived | Analytical 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) |
| crosswalk | twin_pooled | derived | True if this okres is one of the 20 city/rural twin members |
| crosswalk | twin_role | derived | city / ring within the pair (blank off-twin) |
| census | okres_id | region*1000+district | 1910 political-okres id (= districts okres_id; join key) |
| census | census_okres_id | derived | POOLED 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 |
| census | twin_pooled | derived | POOLED panel only: True for the ten collapsed city+ring units |
| census | okres_cs / okres_de | districts | Political okres name, 1910 Czech / German |
| census | lang_* | Volkszählung Umgangssprache | Population by colloquial language: lang_czech (= Böhmisch-Mährisch-Slowakisch bloc), lang_german, ...; lang_total = present citizens. Derived lang_*_share = lang_x / lang_total |
| census | rel_* | Volkszählung Religion | Population by religion: rel_roman_catholic, rel_jewish, rel_lutheran, ...; rel_total. Derived rel_*_share = rel_x / rel_total |
| census | lit_* / disab_* | Volkszählung Bildungsgrad | Literacy 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 |
| census | occ_* | Volkszählung Beruf | Occupation by class (occ_occupation_class_a_land_forestry_*, ...) and status (economically active / workers / servants / self-employed ...) |
| census | pop_* | Volkszählung Einwohner | Localities / houses / households / inhabitants by settlement-size band (pop_lt500_* ... pop_gt20000_*). Derived pop_total = sum of size-class inhabitants |
| census | births_* | Bewegung der Bevölkerung | 1911 births: live/still × legitimate/illegitimate × sex (births_livebirths_*, births_stillbirths_*). Derived illegitimacy_rate = births_livebirths_illegitimate / births_livebirths_total |
| census | deaths_* | Bewegung der Bevölkerung | 1911 deaths by age/sex/legitimacy; deaths_child_* = deaths of children under 5 by age band |
| census | death_* | Bewegung der Bevölkerung — Todesursachen | 1911 deaths by cause × sex (death_tuberculosis_*, death_pneumonia_*, death_suicide_*, ...) |
| census | livestock_* | Viehzählung 1910 | Livestock owners by occupation: livestock_large_* / livestock_small_* / livestock_total_* |
| census | savings_* | Statistik der Sparkassen 1910 | Savings-bank balance sheets (savings_balance_*, Kronen) and passbooks (savings_books_*) |
| census | just_* | Justizstatistik 1910-12 | District-court caseload (just_*) and court personnel (just_personnel_*) |
| census | birth_okres_id | records_geo | Person's birth political-okres id -> census join key (birth_okres via okres+court names, then settlement timeline / unique municipality) |
| census | birth_okres_match | records_geo | Match 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) |
| census | birth_lat / birth_lon | wikidata | Birth-settlement coordinates (WGS84) via birth_municipality -> settlement geocode (~40-50% of records; obce-level) |
| census | birth_qid | wikidata | Wikidata 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
| frame | family | english_column | description | source_column | source_header | source_file |
|---|---|---|---|---|---|---|
| cis | Vital statistics (births) | births_livebirths_legitimate_m | livebirths legitimate m | geb_lebendgeborene_ehelich_m | Lebendgeborene > ehelich > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_livebirths_legitimate_f | livebirths legitimate f | geb_lebendgeborene_ehelich_w | Lebendgeborene > ehelich > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_livebirths_legitimate | livebirths legitimate | geb_lebendgeborene_ehelich_z | Lebendgeborene > ehelich > z. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_livebirths_illegitimate_m | livebirths illegitimate m | geb_lebendgeborene_unehelich_m | Lebendgeborene > unehelich > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_livebirths_illegitimate_f | livebirths illegitimate f | geb_lebendgeborene_unehelich_w | Lebendgeborene > unehelich > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_livebirths_illegitimate | livebirths illegitimate | geb_lebendgeborene_unehelich_z | Lebendgeborene > unehelich > z. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_livebirths_total_m | livebirths total m | geb_lebendgeborene_zusammen_m | Lebendgeborene > zusammen > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_livebirths_total_f | livebirths total f | geb_lebendgeborene_zusammen_w | Lebendgeborene > zusammen > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_livebirths_total | livebirths total | geb_lebendgeborene_zusammen_z | Lebendgeborene > zusammen > z. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_stillbirths_legitimate_m | stillbirths legitimate m | geb_totgeborene_ehelich_m | Totgeborene > ehelich > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_stillbirths_legitimate_f | stillbirths legitimate f | geb_totgeborene_ehelich_w | Totgeborene > ehelich > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_stillbirths_legitimate | stillbirths legitimate | geb_totgeborene_ehelich_z | Totgeborene > ehelich > z. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_stillbirths_illegitimate_m | stillbirths illegitimate m | geb_totgeborene_unehelich_m | Totgeborene > unehelich > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_stillbirths_illegitimate_f | stillbirths illegitimate f | geb_totgeborene_unehelich_w | Totgeborene > unehelich > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_stillbirths_illegitimate | stillbirths illegitimate | geb_totgeborene_unehelich_z | Totgeborene > unehelich > z. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_stillbirths_total_m | stillbirths total m | geb_totgeborene_zusammen_m | Totgeborene > zusammen > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_stillbirths_total_f | stillbirths total f | geb_totgeborene_zusammen_w | Totgeborene > zusammen > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_stillbirths_total | stillbirths total | geb_totgeborene_zusammen_z | Totgeborene > zusammen > z. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_total_number_born_m | total number born m | geb_gesamtzahl_der_geborenen_m | Gesamtzahl der Geborenen > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_total_number_born_f | total number born f | geb_gesamtzahl_der_geborenen_w | Gesamtzahl der Geborenen > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_total_number_born | total number born | geb_gesamtzahl_der_geborenen_z | Gesamtzahl der Geborenen > z. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_roman_catholic_m | religion roman catholic m | geb_religion_romisch_katholisch_m | Religion > römisch-katholisch > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_roman_catholic_f | religion roman catholic f | geb_religion_romisch_katholisch_w | Religion > römisch-katholisch > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_greek_catholic_m | religion greek catholic m | geb_religion_griechisch_katholisch_m | Religion > griechisch-katholisch > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_greek_catholic_f | religion greek catholic f | geb_religion_griechisch_katholisch_w | Religion > griechisch-katholisch > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_greek_orthodox_m | religion greek orthodox m | geb_religion_griechisch_orientalisch_m | Religion > griechisch-orientalisch > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_greek_orthodox_f | religion greek orthodox f | geb_religion_griechisch_orientalisch_w | Religion > griechisch-orientalisch > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_evangelical_m | religion evangelical m | geb_religion_evangelisch_m | Religion > evangelisch > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_evangelical_f | religion evangelical f | geb_religion_evangelisch_w | Religion > evangelisch > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_jewish_m | religion jewish m | geb_religion_israelitisch_m | Religion > israelitisch > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_jewish_f | religion jewish f | geb_religion_israelitisch_w | Religion > israelitisch > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_other_m | religion other m | geb_religion_sonstige_m | Religion > sonstige > m. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Vital statistics (births) | births_religion_other_f | religion other f | geb_religion_sonstige_w | Religion > sonstige > w. | Bewegung der Bevoelkerung Geborene 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_birth_to_5_years_legitimate_m | deceased birth to 5 years legitimate m | gest_gestorbene_von_der_geburt_bis_zu_5_jahren_ehelich_m | Gestorbene von der Geburt bis zu 5 Jahren > ehelich > m. | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_birth_to_5_years_legitimate_f | deceased birth to 5 years legitimate f | gest_gestorbene_von_der_geburt_bis_zu_5_jahren_ehelich_w | Gestorbene von der Geburt bis zu 5 Jahren > ehelich > w. | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_birth_to_5_years_legitimate_sum | deceased birth to 5 years legitimate sum | gest_gestorbene_von_der_geburt_bis_zu_5_jahren_ehelich_summe | Gestorbene von der Geburt bis zu 5 Jahren > ehelich > Summe | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_birth_to_5_years_illegitimate_m | deceased birth to 5 years illegitimate m | gest_gestorbene_von_der_geburt_bis_zu_5_jahren_unehelich_m | Gestorbene von der Geburt bis zu 5 Jahren > unehelich > m. | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_birth_to_5_years_illegitimate_f | deceased birth to 5 years illegitimate f | gest_gestorbene_von_der_geburt_bis_zu_5_jahren_unehelich_w | Gestorbene von der Geburt bis zu 5 Jahren > unehelich > w. | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_birth_to_5_years_illegitimate_sum | deceased birth to 5 years illegitimate sum | gest_gestorbene_von_der_geburt_bis_zu_5_jahren_unehelich_summe | Gestorbene von der Geburt bis zu 5 Jahren > unehelich > Summe | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_birth_to_5_years_total_m | deceased birth to 5 years total m | gest_gestorbene_von_der_geburt_bis_zu_5_jahren_zusammen_m | Gestorbene von der Geburt bis zu 5 Jahren > zusammen > m. | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_birth_to_5_years_total_f | deceased birth to 5 years total f | gest_gestorbene_von_der_geburt_bis_zu_5_jahren_zusammen_w | Gestorbene von der Geburt bis zu 5 Jahren > zusammen > w. | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_birth_to_5_years_total_sum | deceased birth to 5 years total sum | gest_gestorbene_von_der_geburt_bis_zu_5_jahren_zusammen_summe | Gestorbene von der Geburt bis zu 5 Jahren > zusammen > Summe | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_age_over_5_years_m | deceased age over 5 years m | gest_gestorbene_im_alter_uber_5_jahre_m | Gestorbene im Alter über 5 Jahre > m. | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_age_over_5_years_f | deceased age over 5 years f | gest_gestorbene_im_alter_uber_5_jahre_w | Gestorbene im Alter über 5 Jahre > w. | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_deceased_age_over_5_years_sum | deceased age over 5 years sum | gest_gestorbene_im_alter_uber_5_jahre_summe | Gestorbene im Alter über 5 Jahre > Summe | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_total_number_deceased_m | total number deceased m | gest_gesamtzahl_der_gestorbenen_m | Gesamtzahl der Gestorbenen > m. | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_total_number_deceased_f | total number deceased f | gest_gesamtzahl_der_gestorbenen_w | Gesamtzahl der Gestorbenen > w. | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths | deaths_total_number_deceased_sum | total number deceased sum | gest_gesamtzahl_der_gestorbenen_summe | Gesamtzahl der Gestorbenen > Summe | Bewegung der Bevoelkerung Gestorbene im Jahre 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_to_1_month_legitimate_m | to 1 month legitimate m | gest_kind_bis_zu_1_monat_ehelich_m | Bis zu 1 Monat > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_to_1_month_legitimate_f | to 1 month legitimate f | gest_kind_bis_zu_1_monat_ehelich_w | Bis zu 1 Monat > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_to_1_month_illegitimate_m | to 1 month illegitimate m | gest_kind_bis_zu_1_monat_unehelich_m | Bis zu 1 Monat > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_to_1_month_illegitimate_f | to 1 month illegitimate f | gest_kind_bis_zu_1_monat_unehelich_w | Bis zu 1 Monat > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_1_to_2_months_legitimate_m | 1 to 2 months legitimate m | gest_kind_von_1_bis_zu_2_monaten_ehelich_m | Von 1 bis zu 2 Monaten > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_1_to_2_months_legitimate_f | 1 to 2 months legitimate f | gest_kind_von_1_bis_zu_2_monaten_ehelich_w | Von 1 bis zu 2 Monaten > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_1_to_2_months_illegitimate_m | 1 to 2 months illegitimate m | gest_kind_von_1_bis_zu_2_monaten_unehelich_m | Von 1 bis zu 2 Monaten > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_1_to_2_months_illegitimate_f | 1 to 2 months illegitimate f | gest_kind_von_1_bis_zu_2_monaten_unehelich_w | Von 1 bis zu 2 Monaten > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_2_to_3_months_legitimate_m | 2 to 3 months legitimate m | gest_kind_von_2_bis_zu_3_monaten_ehelich_m | Von 2 bis zu 3 Monaten > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_2_to_3_months_legitimate_f | 2 to 3 months legitimate f | gest_kind_von_2_bis_zu_3_monaten_ehelich_w | Von 2 bis zu 3 Monaten > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_2_to_3_months_illegitimate_m | 2 to 3 months illegitimate m | gest_kind_von_2_bis_zu_3_monaten_unehelich_m | Von 2 bis zu 3 Monaten > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_2_to_3_months_illegitimate_f | 2 to 3 months illegitimate f | gest_kind_von_2_bis_zu_3_monaten_unehelich_w | Von 2 bis zu 3 Monaten > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_3_to_6_months_legitimate_m | 3 to 6 months legitimate m | gest_kind_von_3_bis_zu_6_monaten_ehelich_m | Von 3 bis zu 6 Monaten > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_3_to_6_months_legitimate_f | 3 to 6 months legitimate f | gest_kind_von_3_bis_zu_6_monaten_ehelich_w | Von 3 bis zu 6 Monaten > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_3_to_6_months_illegitimate_m | 3 to 6 months illegitimate m | gest_kind_von_3_bis_zu_6_monaten_unehelich_m | Von 3 bis zu 6 Monaten > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_3_to_6_months_illegitimate_f | 3 to 6 months illegitimate f | gest_kind_von_3_bis_zu_6_monaten_unehelich_w | Von 3 bis zu 6 Monaten > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_birth_to_6_months_legitimate_m | birth to 6 months legitimate m | gest_kind_von_der_geburt_bis_zu_6_monaten_ehelich_m | Von der Geburt bis zu 6 Monaten > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_birth_to_6_months_legitimate_f | birth to 6 months legitimate f | gest_kind_von_der_geburt_bis_zu_6_monaten_ehelich_w | Von der Geburt bis zu 6 Monaten > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_birth_to_6_months_illegitimate_m | birth to 6 months illegitimate m | gest_kind_von_der_geburt_bis_zu_6_monaten_unehelich_m | Von der Geburt bis zu 6 Monaten > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_birth_to_6_months_illegitimate_f | birth to 6 months illegitimate f | gest_kind_von_der_geburt_bis_zu_6_monaten_unehelich_w | Von der Geburt bis zu 6 Monaten > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_6_to_9_months_legitimate_m | 6 to 9 months legitimate m | gest_kind_von_6_bis_zu_9_monaten_ehelich_m | Von 6 bis zu 9 Monaten > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_6_to_9_months_legitimate_f | 6 to 9 months legitimate f | gest_kind_von_6_bis_zu_9_monaten_ehelich_w | Von 6 bis zu 9 Monaten > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_6_to_9_months_illegitimate_m | 6 to 9 months illegitimate m | gest_kind_von_6_bis_zu_9_monaten_unehelich_m | Von 6 bis zu 9 Monaten > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_6_to_9_months_illegitimate_f | 6 to 9 months illegitimate f | gest_kind_von_6_bis_zu_9_monaten_unehelich_w | Von 6 bis zu 9 Monaten > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_9_to_12_months_legitimate_m | 9 to 12 months legitimate m | gest_kind_von_9_bis_zu_12_monaten_ehelich_m | Von 9 bis zu 12 Monaten > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_9_to_12_months_legitimate_f | 9 to 12 months legitimate f | gest_kind_von_9_bis_zu_12_monaten_ehelich_w | Von 9 bis zu 12 Monaten > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_9_to_12_months_illegitimate_m | 9 to 12 months illegitimate m | gest_kind_von_9_bis_zu_12_monaten_unehelich_m | Von 9 bis zu 12 Monaten > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_9_to_12_months_illegitimate_f | 9 to 12 months illegitimate f | gest_kind_von_9_bis_zu_12_monaten_unehelich_w | Von 9 bis zu 12 Monaten > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_birth_to_1_year_legitimate_m | birth to 1 year legitimate m | gest_kind_von_der_geburt_bis_zu_1_jahr_ehelich_m | Von der Geburt bis zu 1 Jahr > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_birth_to_1_year_legitimate_f | birth to 1 year legitimate f | gest_kind_von_der_geburt_bis_zu_1_jahr_ehelich_w | Von der Geburt bis zu 1 Jahr > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_birth_to_1_year_illegitimate_m | birth to 1 year illegitimate m | gest_kind_von_der_geburt_bis_zu_1_jahr_unehelich_m | Von der Geburt bis zu 1 Jahr > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_birth_to_1_year_illegitimate_f | birth to 1 year illegitimate f | gest_kind_von_der_geburt_bis_zu_1_jahr_unehelich_w | Von der Geburt bis zu 1 Jahr > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_1_to_2_years_legitimate_m | 1 to 2 years legitimate m | gest_kind_von_1_bis_zu_2_jahren_ehelich_m | Von 1 bis zu 2 Jahren > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_1_to_2_years_legitimate_f | 1 to 2 years legitimate f | gest_kind_von_1_bis_zu_2_jahren_ehelich_w | Von 1 bis zu 2 Jahren > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_1_to_2_years_illegitimate_m | 1 to 2 years illegitimate m | gest_kind_von_1_bis_zu_2_jahren_unehelich_m | Von 1 bis zu 2 Jahren > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_1_to_2_years_illegitimate_f | 1 to 2 years illegitimate f | gest_kind_von_1_bis_zu_2_jahren_unehelich_w | Von 1 bis zu 2 Jahren > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_2_to_3_years_legitimate_m | 2 to 3 years legitimate m | gest_kind_von_2_bis_zu_3_jahren_ehelich_m | Von 2 bis zu 3 Jahren > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_2_to_3_years_legitimate_f | 2 to 3 years legitimate f | gest_kind_von_2_bis_zu_3_jahren_ehelich_w | Von 2 bis zu 3 Jahren > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_2_to_3_years_illegitimate_m | 2 to 3 years illegitimate m | gest_kind_von_2_bis_zu_3_jahren_unehelich_m | Von 2 bis zu 3 Jahren > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_2_to_3_years_illegitimate_f | 2 to 3 years illegitimate f | gest_kind_von_2_bis_zu_3_jahren_unehelich_w | Von 2 bis zu 3 Jahren > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_3_to_4_years_legitimate_m | 3 to 4 years legitimate m | gest_kind_von_3_bis_zu_4_jahren_ehelich_m | Von 3 bis zu 4 Jahren > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_3_to_4_years_legitimate_f | 3 to 4 years legitimate f | gest_kind_von_3_bis_zu_4_jahren_ehelich_w | Von 3 bis zu 4 Jahren > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_3_to_4_years_illegitimate_m | 3 to 4 years illegitimate m | gest_kind_von_3_bis_zu_4_jahren_unehelich_m | Von 3 bis zu 4 Jahren > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_3_to_4_years_illegitimate_f | 3 to 4 years illegitimate f | gest_kind_von_3_bis_zu_4_jahren_unehelich_w | Von 3 bis zu 4 Jahren > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_4_to_5_years_legitimate_m | 4 to 5 years legitimate m | gest_kind_von_4_bis_zu_5_jahren_ehelich_m | Von 4 bis zu 5 Jahren > ehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_4_to_5_years_legitimate_f | 4 to 5 years legitimate f | gest_kind_von_4_bis_zu_5_jahren_ehelich_w | Von 4 bis zu 5 Jahren > ehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_4_to_5_years_illegitimate_m | 4 to 5 years illegitimate m | gest_kind_von_4_bis_zu_5_jahren_unehelich_m | Von 4 bis zu 5 Jahren > unehelich > m. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Deaths under age 5 | deaths_child_4_to_5_years_illegitimate_f | 4 to 5 years illegitimate f | gest_kind_von_4_bis_zu_5_jahren_unehelich_w | Von 4 bis zu 5 Jahren > unehelich > w. | Bewegung der Bevoelkerung Gestorbene Kinder bis zu 5 1911 Political district 406.xlsx |
| cis | Cause of death | death_congenital_congenital_weakness_due_to_early_miscarriage_m | congenital congenital weakness due to early miscarriage m | todes_angeborene_lebensschwache_infolge_von_fruh_oder_missgeburt_m | Angeborene Lebensschwäche infolge von Früh- oder Mißgeburt > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_congenital_congenital_weakness_due_to_early_miscarriage_f | congenital congenital weakness due to early miscarriage f | todes_angeborene_lebensschwache_infolge_von_fruh_oder_missgeburt_w | Angeborene Lebensschwäche infolge von Früh- oder Mißgeburt > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_tuberculosis_lung_other_organs_m | tuberculosis lung other organs m | todes_tuberkulose_der_lungen_oder_anderer_organe_m | Tuberkulose der Lungen oder anderer Organe > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_tuberculosis_lung_other_organs_f | tuberculosis lung other organs f | todes_tuberkulose_der_lungen_oder_anderer_organe_w | Tuberkulose der Lungen oder anderer Organe > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_pneumonia_m | pneumonia m | todes_lungenentzundung_m | Lungenentzündung > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_pneumonia_f | pneumonia f | todes_lungenentzundung_w | Lungenentzündung > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_diphtheria_croup_diphtheria_m | diphtheria croup diphtheria m | todes_diphtherie_krupp_und_diphtherie_m | Diphtherie (Krupp und Diphtherie) > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_diphtheria_croup_diphtheria_f | diphtheria croup diphtheria f | todes_diphtherie_krupp_und_diphtherie_w | Diphtherie (Krupp und Diphtherie) > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_whooping_cough_m | whooping cough m | todes_keuchhusten_m | Keuchhusten > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_whooping_cough_f | whooping cough f | todes_keuchhusten_w | Keuchhusten > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_smallpox_m | smallpox m | todes_blattern_m | Blattern > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_smallpox_f | smallpox f | todes_blattern_w | Blattern > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_scarlet_fever_m | scarlet fever m | todes_scharlach_m | Scharlach > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_scarlet_fever_f | scarlet fever f | todes_scharlach_w | Scharlach > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_measles_m | measles m | todes_masern_m | Masern > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_measles_f | measles f | todes_masern_w | Masern > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_typhus_m | typhus m | todes_flecktyphus_m | Flecktyphus > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_typhus_f | typhus f | todes_flecktyphus_w | Flecktyphus > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_typhoid_m | typhoid m | todes_bauchtyphus_m | Bauchtyphus > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_typhoid_f | typhoid f | todes_bauchtyphus_w | Bauchtyphus > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_dysentery_m | dysentery m | todes_ruhr_m | Ruhr > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_dysentery_f | dysentery f | todes_ruhr_w | Ruhr > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_cholera_m | cholera m | todes_cholera_asiatica_m | Cholera asiatica > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_cholera_f | cholera f | todes_cholera_asiatica_w | Cholera asiatica > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_native_cholera_infantum_children_m | native cholera infantum children m | todes_einheimischer_brechdurchfall_bei_kindern_m | Einheimischer Brechdurchfall bei Kindern > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_native_cholera_infantum_children_f | native cholera infantum children f | todes_einheimischer_brechdurchfall_bei_kindern_w | Einheimischer Brechdurchfall bei Kindern > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_native_cholera_infantum_except_childhood_m | native cholera infantum except childhood m | todes_einheimischer_brechdurchfall_ausser_dem_kindesalter_m | Einheimischer Brechdurchfall außer dem Kindesalter > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_native_cholera_infantum_except_childhood_f | native cholera infantum except childhood f | todes_einheimischer_brechdurchfall_ausser_dem_kindesalter_w | Einheimischer Brechdurchfall außer dem Kindesalter > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_puerperal_fever_m | puerperal fever m | todes_kindbettfieber_m | Kindbettfieber > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_puerperal_fever_f | puerperal fever f | todes_kindbettfieber_w | Kindbettfieber > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_wound_infections_m | wound infections m | todes_wundinfektionskrankheiten_m | Wundinfektionskrankheiten > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_wound_infections_f | wound infections f | todes_wundinfektionskrankheiten_w | Wundinfektionskrankheiten > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_other_infectious_diseases_m | other infectious diseases m | todes_andere_infektionskrankheiten_m | Andere Infektionskrankheiten > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_other_infectious_diseases_f | other infectious diseases f | todes_andere_infektionskrankheiten_w | Andere Infektionskrankheiten > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_communicable_animal_diseases_m | communicable animal diseases m | todes_ubertragbare_tierkrankheiten_m | Übertragbare Tierkrankheiten > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_communicable_animal_diseases_f | communicable animal diseases f | todes_ubertragbare_tierkrankheiten_w | Übertragbare Tierkrankheiten > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_stroke_m | stroke m | todes_gehirnschlagfluss_m | Gehirnschlagfluß > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_stroke_f | stroke f | todes_gehirnschlagfluss_w | Gehirnschlagfluß > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_organic_heart_disease_diseases_vascular_m | organic heart disease diseases vascular m | todes_organische_herzkrankheiten_und_krankheiten_der_blutgefasse_m | Organische Herzkrankheiten und Krankheiten der Blutgefäße > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_organic_heart_disease_diseases_vascular_f | organic heart disease diseases vascular f | todes_organische_herzkrankheiten_und_krankheiten_der_blutgefasse_w | Organische Herzkrankheiten und Krankheiten der Blutgefäße > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_malignant_neoplasms_m | malignant neoplasms m | todes_bosartige_neubildungen_m | Bösartige Neubildungen > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_malignant_neoplasms_f | malignant neoplasms f | todes_bosartige_neubildungen_w | Bösartige Neubildungen > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_other_natural_cause_of_death_m | other natural cause of death m | todes_sonstige_naturliche_todesursachen_m | Sonstige natürliche Todesursachen > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_other_natural_cause_of_death_f | other natural cause of death f | todes_sonstige_naturliche_todesursachen_w | Sonstige natürliche Todesursachen > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_accidental_injuries_m | accidental injuries m | todes_zufallige_beschadigungen_m | Zufällige Beschädigungen > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_accidental_injuries_f | accidental injuries f | todes_zufallige_beschadigungen_w | Zufällige Beschädigungen > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_suicide_m | suicide m | todes_selbstmord_m | Selbstmord > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_suicide_f | suicide f | todes_selbstmord_w | Selbstmord > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_murder_manslaughter_m | murder manslaughter m | todes_mord_und_totschlag_m | Mord und Totschlag > m. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Cause of death | death_murder_manslaughter_f | murder manslaughter f | todes_mord_und_totschlag_w | Mord und Totschlag > w. | Bewegung der Bevoelkerung Todesursachen 1911 Political district 406.xlsx |
| cis | Justice — district-court caseload | just_dunning_issued_payment_orders | dunning issued payment orders | just_bg_mahnsachen_erlassene_zahlungsbefehle | Mahnsachen > Erlassene Zahlungsbefehle | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_dunning_raised_accepted_objections | dunning raised accepted objections | just_bg_mahnsachen_erhobene_und_angenommene_widerspruche | Mahnsachen > Erhobene und angenommene Widersprüche | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_order_procedure_issued_payment_orders_objections_not_raised | order procedure issued payment orders objections not raised | just_bg_mandatsverfahren_erlassene_zahlungsauftrage_gegen_welche_eindungen_nicht_erhoben_wurden | Mandatsverfahren > erlassene Zahlungsaufträge, gegen welche Eindungen nicht erhoben wurden | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_lawsuits_prior_year_pending_carried_over | small claims lawsuits prior year pending carried over | just_bg_iii_bagatellverfahren_klagen_vom_vorjahre_anhangig_ubernommen | III. Bagatellverfahren > Klagen > vom Vorjahre anhängig übernommen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_lawsuits_new_accrued | small claims lawsuits new accrued | just_bg_iii_bagatellverfahren_klagen_neu_zugewachsen | III. Bagatellverfahren > Klagen > neu zugewachsen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_lawsuits_settled | small claims lawsuits settled | just_bg_iii_bagatellverfahren_klagen_erledigt | III. Bagatellverfahren > Klagen > erledigt | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_lawsuits_pending_remaining | small claims lawsuits pending remaining | just_bg_iii_bagatellverfahren_klagen_anhangig_verblieben | III. Bagatellverfahren > Klagen > anhängig verblieben | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_disposition_final_judgment_grounds_default_waiver_acknowledgment | small claims disposition final judgment grounds default waiver acknowledgment | just_bg_iii_bagatellverfahren_erledigungsart_durch_endurteil_auf_grund_versaumnis_verzicht_anerkenntnnis | III. Bagatellverfahren > Erledigungsart > durch Endurteil auf Grund Versäumnis, Verzicht, Anerkenntnnis | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_disposition_other_final_judgment | small claims disposition other final judgment | just_bg_iii_bagatellverfahren_erledigungsart_durch_anderes_endurteil | III. Bagatellverfahren > Erledigungsart > durch anderes Endurteil | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_disposition_settlement | small claims disposition settlement | just_bg_iii_bagatellverfahren_erledigungsart_durch_vergleich | III. Bagatellverfahren > Erledigungsart > durch Vergleich | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_disposition_other_manner | small claims disposition other manner | just_bg_iii_bagatellverfahren_erledigungsart_auf_andere_weise | III. Bagatellverfahren > Erledigungsart > auf andere Weise | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_duration_proceedings_falls_8_to_10_to_1_month | small claims duration proceedings falls 8 to 10 to 1 month | just_bg_iii_bagatellverfahren_dauer_des_verfahrens_in_den_fallen_der_spalten_8_bis_10_bis_1_monat | III. Bagatellverfahren > Dauer des Verfahrens in den Fällen der Spalten 8 bis 10 > bis 1 Monat | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_duration_proceedings_falls_8_to_10_over_1_to_3_months | small claims duration proceedings falls 8 to 10 over 1 to 3 months | just_bg_iii_bagatellverfahren_dauer_des_verfahrens_in_den_fallen_der_spalten_8_bis_10_uber_1_bis_3_monate | III. Bagatellverfahren > Dauer des Verfahrens in den Fällen der Spalten 8 bis 10 > über 1 bis 3 Monate | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_duration_proceedings_falls_8_to_10_over_3_to_6_months | small claims duration proceedings falls 8 to 10 over 3 to 6 months | just_bg_iii_bagatellverfahren_dauer_des_verfahrens_in_den_fallen_der_spalten_8_bis_10_uber_3_bis_6_monate | III. Bagatellverfahren > Dauer des Verfahrens in den Fällen der Spalten 8 bis 10 > über 3 bis 6 Monate | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_small_claims_duration_proceedings_falls_8_to_10_over_6_months | small claims duration proceedings falls 8 to 10 over 6 months | just_bg_iii_bagatellverfahren_dauer_des_verfahrens_in_den_fallen_der_spalten_8_bis_10_uber_6_monate | III. Bagatellverfahren > Dauer des Verfahrens in den Fällen der Spalten 8 bis 10 > über 6 Monate | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_lawsuits_prior_year_pending_carried_over | other trials lawsuits prior year pending carried over | just_bg_andere_prozesse_klagen_vom_vorjahre_anhangig_ubernommen | Andere Prozesse > Klagen > vom Vorjahre anhängig übernommen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_lawsuits_new_accrued_money_monetary_value_over_100_to_1000 | other trials lawsuits new accrued money monetary value over 100 to 1000 | just_bg_andere_prozesse_klagen_neu_zugewachsen_geld_oder_geldeswert_uber_100_k_bis_1000_k | Andere Prozesse > Klagen > neu zugewachsen > Geld oder Geldeswert über 100 K bis 1000 K | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_lawsuits_new_accrued_possession_disputes | other trials lawsuits new accrued possession disputes | just_bg_andere_prozesse_klagen_neu_zugewachsen_besitzstorungsstreitigkeiten | Andere Prozesse > Klagen > neu zugewachsen > Besitzstörungsstreitigkeiten | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_lawsuits_new_accrued_other_disputes | other trials lawsuits new accrued other disputes | just_bg_andere_prozesse_klagen_neu_zugewachsen_andere_streitigkeiten | Andere Prozesse > Klagen > neu zugewachsen > andere Streitigkeiten | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_lawsuits_settled | other trials lawsuits settled | just_bg_andere_prozesse_klagen_erledigt | Andere Prozesse > Klagen > erledigt | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_lawsuits_pending_remaining | other trials lawsuits pending remaining | just_bg_andere_prozesse_klagen_anhangig_verblieben | Andere Prozesse > Klagen > anhängig verblieben | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_disposition_final_judgment_grounds_default_waiver_acknowledgment | other trials disposition final judgment grounds default waiver acknowledgment | just_bg_andere_prozesse_erledigungsart_durch_endurteil_auf_grund_von_versaumnis_verzicht_anerkenntnis | Andere Prozesse > Erledigungsart > durch Endurteil auf Grund von Versäumnis, Verzicht, Anerkenntnis | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_disposition_other_final_judgment_final_order | other trials disposition other final judgment final order | just_bg_andere_prozesse_erledigungsart_durch_anderes_endurteil_endbeschluss | Andere Prozesse > Erledigungsart > durch anderes Endurteil (Endbeschluß) | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_disposition_settlement | other trials disposition settlement | just_bg_andere_prozesse_erledigungsart_durch_vergleich | Andere Prozesse > Erledigungsart > durch Vergleich | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_disposition_other_manner | other trials disposition other manner | just_bg_andere_prozesse_erledigungsart_auf_andere_weise | Andere Prozesse > Erledigungsart > auf andere Weise | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_cases_proceedings_suspended | other trials cases proceedings suspended | just_bg_andere_prozesse_falle_in_welchen_das_verfahren_ruhte | Andere Prozesse > Fälle, in welchen das Verfahren ruhte | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_evidentiary_orders_final_examination_parties | other trials evidentiary orders final examination parties | just_bg_andere_prozesse_beweisbeschlusse_auf_endliche_vernehmung_der_parteien | Andere Prozesse > Beweisbeschlüsse auf endliche Vernehmung der Parteien | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_other_evidentiary_orders | other trials other evidentiary orders | just_bg_andere_prozesse_andere_beweisbeschlusse | Andere Prozesse > andere Beweisbeschlüsse | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_duration_proceedings_falls_to_to_1_month | other trials duration proceedings falls to 1 month | just_bg_andere_prozesse_dauer_des_verfahrens_in_den_fallen_der_spalten_v22_bis_v24_bis_1_monat | Andere Prozesse > Dauer des Verfahrens in den Fällen der Spalten V22 bis V24 > bis 1 Monat | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_duration_proceedings_falls_to_over_1_to_3_months | other trials duration proceedings falls to over 1 to 3 months | just_bg_andere_prozesse_dauer_des_verfahrens_in_den_fallen_der_spalten_v22_bis_v24_uber_1_bis_3_monate | Andere Prozesse > Dauer des Verfahrens in den Fällen der Spalten V22 bis V24 > über 1 bis 3 Monate | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_duration_proceedings_falls_to_over_3_to_6_months | other trials duration proceedings falls to over 3 to 6 months | just_bg_andere_prozesse_dauer_des_verfahrens_in_den_fallen_der_spalten_v22_bis_v24_uber_3_bis_6_monate | Andere Prozesse > Dauer des Verfahrens in den Fällen der Spalten V22 bis V24 > über 3 bis 6 Monate | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_duration_proceedings_falls_to_over_6_months_to_1_year | other trials duration proceedings falls to over 6 months to 1 year | just_bg_andere_prozesse_dauer_des_verfahrens_in_den_fallen_der_spalten_v22_bis_v24_uber_6_monate_bis_1_jahr | Andere Prozesse > Dauer des Verfahrens in den Fällen der Spalten V22 bis V24 > über 6 Monate bis 1 Jahr | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_other_trials_duration_proceedings_falls_to_over_1_year | other trials duration proceedings falls to over 1 year | just_bg_andere_prozesse_dauer_des_verfahrens_in_den_fallen_der_spalten_v22_bis_v24_uber_1_jahr | Andere Prozesse > Dauer des Verfahrens in den Fällen der Spalten V22 bis V24 > über 1 Jahr | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_total_cases | enforcement total cases | just_bg_zwangsvollstreckungen_gesamtfall | Zwangsvollstreckungen > Gesamtfall | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_executions_recovery_fines_fees | enforcement executions recovery fines fees | just_bg_zwangsvollstreckungen_exekutionen_zur_hereinbringung_von_geldstrafen_und_gebuhren | Zwangsvollstreckungen > Exekutionen zur Hereinbringung von Geldstrafen und Gebühren | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_immovable_assets_compulsory_lien_creation | enforcement immovable assets compulsory lien creation | just_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsweise_pfandrechtsbegrundung | Zwangsvollstreckungen > auf unbeweghches Vermögen > durch zwangsweise Pfandrechtsbegründung | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_immovable_assets_forced_auction_prior_year_pending_carried_over | enforcement immovable assets forced auction prior year pending carried over | just_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_vom_vorjahre_anhangig_ubernommen | Zwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > vom Vorjahre anhängig übernommen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_immovable_assets_forced_auction_new_accruals | enforcement immovable assets forced auction new accruals | just_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_neu_zuwachsen | Zwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > neu zuwachsen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_immovable_assets_forced_auction_concluded_issuance_distribution_order | enforcement immovable assets forced auction concluded issuance distribution order | just_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_beendet_durch_erlassung_verteilungsbeschlusses | Zwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > beendet durch Erlassung Verteilungsbeschlusses | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_immovable_assets_forced_auction_concluded_discontinuation | enforcement immovable assets forced auction concluded discontinuation | just_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_beendet_durch_einstellung | Zwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > beendet durch Einstellung | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_immovable_assets_forced_auction_pending_remaining | enforcement immovable assets forced auction pending remaining | just_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_anhangig_verblieben | Zwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > anhängig verblieben | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_immovable_assets_forced_auction_cases_discontinuation_auction_proceedings_6_months | enforcement immovable assets forced auction cases discontinuation auction proceedings 6 months | just_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsversteigerung_falle_der_einstellung_des_versteigerungsverfahrens_fur_wenigstens_6_monate | Zwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsversteigerung > Fälle der Einstellung des Versteigerungsverfahrens für wenigstens 6 Monate | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_immovable_assets_compulsory_administration_prior_year_carried_over | enforcement immovable assets compulsory administration prior year carried over | just_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsverwaltung_vom_vorjahre_ubernommen | Zwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsverwaltung > vom Vorjahre übernommen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_immovable_assets_compulsory_administration_new_accrued | enforcement immovable assets compulsory administration new accrued | just_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsverwaltung_neu_zugewachsen | Zwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsverwaltung > neu zugewachsen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_immovable_assets_compulsory_administration_concluded | enforcement immovable assets compulsory administration concluded | just_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsverwaltung_beendet | Zwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsverwaltung > beendet | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_immovable_assets_compulsory_administration_pending_remaining | enforcement immovable assets compulsory administration pending remaining | just_bg_zwangsvollstreckungen_auf_unbeweghches_vermogen_durch_zwangsverwaltung_anhangig_verblieben | Zwangsvollstreckungen > auf unbeweghches Vermögen > durch Zwangsverwaltung > anhängig verblieben | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_movable_assets_physical_matters | enforcement movable assets physical matters | just_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_korperliche_sachen | Zwangsvollstreckungen > auf bewegliches Vermögen > körperliche Sachen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_movable_assets_monetary_claims | enforcement movable assets monetary claims | just_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_geldforderungen | Zwangsvollstreckungen > auf bewegliches Vermögen > Geldforderungen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_movable_assets_claims_surrender_performance_physical_matters | enforcement movable assets claims surrender performance physical matters | just_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_anspruche_auf_herausgabe_und_leistung_korperlicher_sachen | Zwangsvollstreckungen > auf bewegliches Vermögen > Ansprüche auf Herausgabe und Leistung körperlicher Sachen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_movable_assets_other_property_rights_compulsory_administration_prior_year_carried_over | enforcement movable assets other property rights compulsory administration prior year carried over | just_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_auf_andere_vermogensrechte_durch_zwangsverwaltung_vom_vorjahre_ubernommen | Zwangsvollstreckungen > auf bewegliches Vermögen > auf andere Vermögensrechte > durch Zwangsverwaltung > vom Vorjahre übernommen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_movable_assets_other_property_rights_compulsory_administration_new_accrued | enforcement movable assets other property rights compulsory administration new accrued | just_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_auf_andere_vermogensrechte_durch_zwangsverwaltung_neu_zugewachsen | Zwangsvollstreckungen > auf bewegliches Vermögen > auf andere Vermögensrechte > durch Zwangsverwaltung > neu zugewachsen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_movable_assets_other_property_rights_compulsory_administration_concluded | enforcement movable assets other property rights compulsory administration concluded | just_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_auf_andere_vermogensrechte_durch_zwangsverwaltung_beendet | Zwangsvollstreckungen > auf bewegliches Vermögen > auf andere Vermögensrechte > durch Zwangsverwaltung > beendet | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_movable_assets_other_property_rights_compulsory_administration_pending_remaining | enforcement movable assets other property rights compulsory administration pending remaining | just_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_auf_andere_vermogensrechte_durch_zwangsverwaltung_anhangig_verblieben | Zwangsvollstreckungen > auf bewegliches Vermögen > auf andere Vermögensrechte > durch Zwangsverwaltung > anhängig verblieben | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_movable_assets_other_property_rights_other_enforcement_means | enforcement movable assets other property rights other enforcement means | just_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_auf_andere_vermogensrechte_durch_andere_exekutionsmittel | Zwangsvollstreckungen > auf bewegliches Vermögen > auf andere Vermögensrechte > durch andere Exekutionsmittel | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_movable_assets_conducted_seizure_physical_matters | enforcement movable assets conducted seizure physical matters | just_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_vorgenommene_pfandung_korperlicher_sachen | Zwangsvollstreckungen > auf bewegliches Vermögen > vorgenommene Pfändung körperlicher Sachen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_movable_assets_conducted_forced_sales | enforcement movable assets conducted forced sales | just_bg_zwangsvollstreckungen_auf_bewegliches_vermogen_vorgenommene_zwangsverkaufe | Zwangsvollstreckungen > auf bewegliches Vermögen > vorgenommene Zwangsverkäufe | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_obtainment_acts_omissions | enforcement obtainment acts omissions | just_bg_zwangsvollstreckungen_zur_erwirkung_von_handlungen_und_unterlassungen | Zwangsvollstreckungen > zur Erwirkung von Handlungen und Unterlassungen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_security_monetary_claims | enforcement security monetary claims | just_bg_zwangsvollstreckungen_zur_sicherung_von_geldforderungen | Zwangsvollstreckungen > zur Sicherung von Geldforderungen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_enforcement_eviction_tenancy_matters | enforcement eviction tenancy matters | just_bg_zwangsvollstreckungen_zur_raumung_von_bestandsachen | Zwangsvollstreckungen > zur Räumung von Bestandsachen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_probate_prior_year_pending_carried_over | probate prior year pending carried over | just_bg_verlassenschaftsabhandlungen_vom_vorjahre_anhangig_ubernommen | Verlassenschaftsabhandlungen > vom Vorjahre anhängig übernommen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_probate_concluded | probate concluded | just_bg_verlassenschaftsabhandlungen_beendet | Verlassenschaftsabhandlungen > beendet | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_probate_pending_remaining_judicial_proceedings_accruals | probate pending remaining judicial proceedings accruals | just_bg_verlassenschaftsabhandlungen_anhangig_verblieben_gerichtliches_verfahren_im_zuge | Verlassenschaftsabhandlungen > anhängig verblieben > gerichtliches Verfahren im Zuge | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_probate_pending_remaining_judicial_steps_concluded_estate_award_fee_adjustment_dependent | probate pending remaining judicial steps concluded estate award fee adjustment dependent | just_bg_verlassenschaftsabhandlungen_anhangig_verblieben_die_gerichtlichen_schritte_beendet_und_die_einantwortung_nur_mehr_von_der_gebuhrenberichtigung_abhangig | Verlassenschaftsabhandlungen > anhängig verblieben > die gerichtlichen Schritte beendet und die Einantwortung nur mehr von der Gebührenberichtigung abhängig | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_probate_disposition_disposed_concerning_outgoing_assets | probate disposition disposed concerning outgoing assets | just_bg_verlassenschaftsabhandlungen_beendigungsart_abgetan_wegen_abgang_eines_vermogens | Verlassenschaftsabhandlungen > Beendigungsart > abgetan wegen Abgang eines Vermögens | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_probate_disposition_de_jure_credits_estate_award | probate disposition de jure credits estate award | just_bg_verlassenschaftsabhandlungen_beendigungsart_durch_jure_crediti_einantwortung | Verlassenschaftsabhandlungen > Beendigungsart > durch jure crediti Einantwortung | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_probate_disposition_estate_award_heir | probate disposition estate award heir | just_bg_verlassenschaftsabhandlungen_beendigungsart_durch_einantwortung_an_die_erbe | Verlassenschaftsabhandlungen > Beendigungsart > durch Einantwortung an die Erbe | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_probate_disposition_handover_escheat_estate_treasury | probate disposition handover escheat estate treasury | just_bg_verlassenschaftsabhandlungen_beendigungsart_durch_ubergabe_des_kaduken_nachlasses_an_den_fiskus | Verlassenschaftsabhandlungen > Beendigungsart > durch Übergabe des kaduken Nachlasses an den Fiskus | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_probate_disposition_other_manner | probate disposition other manner | just_bg_verlassenschaftsabhandlungen_beendigungsart_auf_andere_weise | Verlassenschaftsabhandlungen > Beendigungsart > auf andere Weise | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_guardianships_prior_year_pending_carried_over | guardianships prior year pending carried over | just_bg_vormundschaften_vom_vorjahre_anhangig_ubernommen | Vormundschaften > vom Vorjahre anhängig übernommen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_guardianships_new_accrued | guardianships new accrued | just_bg_vormundschaften_neu_zugewachsen | Vormundschaften > neu zugewachsen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_guardianships_concluded | guardianships concluded | just_bg_vormundschaften_beendet | Vormundschaften > beendet | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_guardianships_pending_remaining | guardianships pending remaining | just_bg_vormundschaften_anhangig_verblieben | Vormundschaften > anhängig verblieben | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_curatorships_prior_year_pending_carried_over | curatorships prior year pending carried over | just_bg_kuratelen_vom_vorjahre_anhangig_ubernom_men | Kuratelen > vom Vorjahre anhängig übernom- men | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_curatorships_new_accrued_concerning_insanity_idiocy_concerning_incapacity_rights_self_preserve | curatorships new accrued concerning insanity idiocy concerning incapacity rights self preserve | just_bg_kuratelen_neu_zugewachsen_wegen_wahn_blodsinns_und_wegen_unfahigkeit_die_rechte_selbst_zu_wahren | Kuratelen > neu zugewachsen > wegen Wahn- Blödsinns und wegen Unfähigkeit, die Rechte selbst zu wahren | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_curatorships_new_accrued_concerning_prodigality | curatorships new accrued concerning prodigality | just_bg_kuratelen_neu_zugewachsen_wegen_verschwendung | Kuratelen > neu zugewachsen > wegen Verschwendung | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_curatorships_new_accrued_over_minors_under_paternal_violence | curatorships new accrued over minors under paternal violence | just_bg_kuratelen_neu_zugewachsen_uber_minderjahrige_unter_vaterlicher_gewalt | Kuratelen > neu zugewachsen > über Minderjährige unter väterlicher Gewalt | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_curatorships_new_accrued_concerning_extension_paternal_violence | curatorships new accrued concerning extension paternal violence | just_bg_kuratelen_neu_zugewachsen_wegen_verlangerung_der_vaterlichen_gewalt | Kuratelen > neu zugewachsen > wegen Verlängerung der väterlichen Gewalt | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_curatorships_concluded | curatorships concluded | just_bg_kuratelen_beendet | Kuratelen > beendet | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_curatorships_pending_remaining | curatorships pending remaining | just_bg_kuratelen_anhangig_verblieben | Kuratelen > anhängig verblieben | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_land_registry | land registry | just_bg_grundbuchssachen | Grundbuchssachen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_termination_proceedings_tenancy_matters_terminations | termination proceedings tenancy matters terminations | just_bg_kundigungsverfahren_in_bestandangelegenheiten_kundigungen | Kündigungsverfahren in Bestandangelegenheiten > Kündigungen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_termination_proceedings_tenancy_matters_objections | termination proceedings tenancy matters objections | just_bg_kundigungsverfahren_in_bestandangelegenheiten_heinwendungen_dagegen | Kündigungsverfahren in Bestandangelegenheiten > HEinwendungen dagegen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_matters_general_register | matters general register | just_bg_sachen_des_allgemeinen_registers | Sachen des allgemeinen Registers | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_commissioned_official_acts_outside_office_location_judicial_officials | commissioned official acts outside office location judicial officials | just_bg_kommissionelle_amtshandlungen_ausserhalb_des_amtsortes_der_richterlichen_beamten | Kommissionelle Amtshandlungen außerhalb des Amtsortes > der richterlichen Beamten | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_commissioned_official_acts_outside_office_location_clerks | commissioned official acts outside office location clerks | just_bg_kommissionelle_amtshandlungen_ausserhalb_des_amtsortes_der_kanzleibeamten | Kommissionelle Amtshandlungen außerhalb des Amtsortes > der Kanzleibeamten | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_summonses_conciliation_attempts_by_433_0 | summonses conciliation attempts by 433 0 | just_bg_vorladungen_zum_vergleichsversuche_nach_433_z_p_0 | Vorladungen zum Vergleichsversuche nach § 433 Z. P. 0. | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_probate_new_accrued | probate new accrued | just_bg_verlassenschaftsabhandlungen_neu_zugewachsen | Verlassenschaftsabhandlungen > neu zugewachsen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_probate_disposition_ceded | probate disposition ceded | just_bg_verlassenschaftsabhandlungen_beendigungsart_abgetreten | Verlassenschaftsabhandlungen > Beendigungsart > abgetreten | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_legal_aid | legal aid | just_bg_rechtshufesachen | Rechtshüfesachen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_legalizations | legalizations | just_bg_legalisierungen | Legalisierungen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — district-court caseload | just_administrative | administrative | just_bg_verwaltungssachen | Verwaltungssachen | Justizstatistik Bezirksgericht 1910-1912 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_area | area | just_pers_flachen | Flachen | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_municipalities | municipalities | just_pers_ortsgemeinden | Ortsgemeinden | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_localities | localities | just_pers_ortschaften | Ortschaften | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_civilian_population | civilian population | just_pers_zivilbevolkerung | Zivilbevolkerung | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_pop_total | pop total | just_pers_gesamtbevolkerung | Gesamtbevolkerung | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_pop_total_attributable_inhabitants_1_km2 | pop total attributable inhabitants 1 km2 | just_pers_von_der_gesamtbevolkerung_entfallen_einwohner_auf_1_km2 | Von der Gesamtbevölkerung entfallen Einwohner auf 1 km2 | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_judicial_officials | judicial officials | just_pers_richterliche_beamte | Richterliche Beamte | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_clerks | clerks | just_pers_kanzleibeamte | Kanzleibeamte | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_subordinate_officials | subordinate officials | just_pers_unterbeamte | Unterbeamte | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_office_servants | office servants | just_pers_amtsdiener | Amtsdiener | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_clerical_staff | clerical staff | just_pers_kanzleihilfspersonal | KanzleiHilfspersonal | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_auxiliary_servants | auxiliary servants | just_pers_aushilfsdiener | Aushilfsdiener | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_advocates | advocates | just_pers_advokaten | Advokaten | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Justice — court personnel | just_personnel_notaries_established_stellen | notaries established stellen | just_pers_notare_systemisierte_stellen | Notare (systemisierte Stellen) | Justizstatistik Personal bei Bezirksgerichte 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_liabilities_deposits_crowns | liabilities deposits crowns | spar_bil_passiva_einlagenstand_kronen | Passiva > Einlagenstand > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_liabilities_reserve_fund_crowns | liabilities reserve fund crowns | spar_bil_passiva_reservefonds_kronen | Passiva > Reservefonds > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_liabilities_special_reserve_fund_crowns | liabilities special reserve fund crowns | spar_bil_passiva_spezialreservefonds_kronen | Passiva > Spezialreservefonds > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_liabilities_other_crowns | liabilities other crowns | spar_bil_passiva_sonstige_kronen | Passiva > Sonstige > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_liabilities_included_pension_fund_a_reserve_fund_b_special_reserve_fund_c_other_liabilities_crowns | liabilities included pension fund a reserve fund b special reserve fund c other liabilities crowns | spar_bil_passiva_es_sind_enthalten_pensionsfonds_in_a_reservedonds_b_spezialreservefonds_c_sonstiger_passiva_kronen | Passiva > Es sind enthalten > Pensionsfonds > in a) Reservedonds, b) Spezialreservefonds, c) sonstiger Passiva > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_liabilities_included_endowment_advance_fund_a_reserve_fund_b_special_reserve_fund_c_other_liabilities_crowns | liabilities included endowment advance fund a reserve fund b special reserve fund c other liabilities crowns | spar_bil_passiva_es_sind_enthalten_dotation_der_vorschusskassa_in_a_reservedonds_b_spezialreservefonds_c_sonstiger_passiva_kronen | Passiva > Es sind enthalten > Dotation der Vorschußkassa > in a) Reservedonds, b) Spezialreservefonds, c) sonstiger Passiva > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_liabilities_sum_crowns | liabilities sum crowns | spar_bil_passiva_summe_kronen | Passiva > Summe > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_mortgage_loans_crowns | assets mortgage loans crowns | spar_bil_aktiva_hypothekardarlehen_kronen | Aktiva > Hypothekardarlehen > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_loans_municipalities_districts_real_mortgage_crowns | assets loans municipalities districts real mortgage crowns | spar_bil_aktiva_darlehen_an_gemeinden_bezirke_usw_gegen_real_hypothek_kronen | Aktiva > Darlehen an Gemeinden, Bezirke, usw. > gegen Real-hypothek > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_loans_municipalities_districts_without_crowns | assets loans municipalities districts without crowns | spar_bil_aktiva_darlehen_an_gemeinden_bezirke_usw_ohne_dieselbe_kronen | Aktiva > Darlehen an Gemeinden, Bezirke, usw. > ohne dieselbe > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_bills_on_hand_crowns | assets bills on hand crowns | spar_bil_aktiva_wechselvorrat_kronen | Aktiva > Wechselvorrat > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_advances_securities_pledges_crowns | assets advances securities pledges crowns | spar_bil_aktiva_vorschosse_auf_wertpapiere_und_pfander_kronen | Aktiva > Vorschósse auf Wertpapiere und Pfänder > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_advances_personal_credit_crowns | assets advances personal credit crowns | spar_bil_aktiva_vorschusse_auf_personalkredit_kronen | Aktiva > Vorschüsse auf Personalkredit > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_market_value_securities_crowns | assets market value securities crowns | spar_bil_aktiva_kurswert_der_wertpapiere_kronen | Aktiva > Kurswert der Wertpapiere > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_value_own_real_estate_crowns | assets value own real estate crowns | spar_bil_aktiva_wert_der_eigenen_realitaten_kronen | Aktiva > Wert der eigenen Realitäten > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_temporary_annexes_credit_institutions_crowns | assets temporary annexes credit institutions crowns | spar_bil_aktiva_zeitliche_anlagen_bei_kreditinstituten_etc_kronen | Aktiva > Zeitliche Anlagen bei Kreditinstituten etc. > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_cash_crowns | assets cash crowns | spar_bil_aktiva_kassabarschaft_kronen | Aktiva > Kassabarschaft > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_other_crowns | assets other crowns | spar_bil_aktiva_sonstige_kronen | Aktiva > Sonstige > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (balance sheets) | savings_balance_assets_sum_crowns | assets sum crowns | spar_bil_aktiva_summe_kronen | Aktiva > Summe > Kronen | Statistik der Sparkassen Bilanzen 1910 District courts 934.xlsx |
| cis | Savings banks (passbooks) | savings_books_in_circulation_start_of_year | savings books in circulation at the start of the year | spar_buch_zahl_der_sparkassebucher_einleger_interessenten_im_anfang_des_jahres_im_umlauf | Zahl der Sparkassebücher (Einleger, Interessenten) > im Anfang des Jahres im Umlauf | Statistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx |
| cis | Savings banks (passbooks) | savings_books_number_savings_books_depositors_stakeholders_year_new_issued | number savings books depositors stakeholders year new issued | spar_buch_zahl_der_sparkassebucher_einleger_interessenten_im_laufe_des_jahres_neu_ausgegebene | Zahl der Sparkassebücher (Einleger, Interessenten) > im Laufe des Jahres > neu ausgegebene | Statistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx |
| cis | Savings banks (passbooks) | savings_books_number_savings_books_depositors_stakeholders_year_repaid | number savings books depositors stakeholders year repaid | spar_buch_zahl_der_sparkassebucher_einleger_interessenten_im_laufe_des_jahres_getilgte | Zahl der Sparkassebücher (Einleger, Interessenten) > im Laufe des Jahres > getilgte | Statistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx |
| cis | Savings banks (passbooks) | savings_books_in_circulation_end_of_year | savings books in circulation at the end of the year | spar_buch_zahl_der_sparkassebucher_einleger_interessenten_zu_ende_des_jahres_im_umlauf | Zahl der Sparkassebücher (Einleger, Interessenten) > zu Ende des Jahres im Umlauf | Statistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx |
| cis | Savings banks (passbooks) | savings_books_partial_deposits_items | partial deposits during the year, items | spar_buch_ausserdem_im_laufe_des_jahres_erfolgte_teil_ein_zahlungen_in_posten | Außerdem im Laufe des Jahres erfolgte > Teil-Ein- > zahlungen in Posten | Statistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx |
| cis | Savings banks (passbooks) | savings_books_partial_withdrawals_items | partial withdrawals during the year, items | spar_buch_ausserdem_im_laufe_des_jahres_erfolgte_teil_ruck_zahlungen_in_posten | Außerdem im Laufe des Jahres erfolgte > Teil-Rück- > zahlungen in Posten | Statistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx |
| cis | Savings banks (passbooks) | savings_books_sum_deposits_items | sum of deposits, items | spar_buch_summe_der_ein_zahlungen_in_posten | Summe der > Ein- > zahlungen in Posten | Statistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx |
| cis | Savings banks (passbooks) | savings_books_sum_withdrawals_items | sum of withdrawals, items | spar_buch_summe_der_ruck_zahlungen_in_posten | Summe der > Rück- > zahlungen in Posten | Statistik der Sparkassen Sparkassebücher 1910_District courts 934.xlsx |
| cis | Livestock (large) | livestock_large_owners_horses_occupation_members_agriculture | owners horses occupation members agriculture | vieh_gross_besitzer_von_pferden_berufszugehorige_landwirtschaft | Besitzer von Pferden > Berufszugehörige > Landwirtschaft | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_horses_occupation_members_industry | owners horses occupation members industry | vieh_gross_besitzer_von_pferden_berufszugehorige_industrie | Besitzer von Pferden > Berufszugehörige > Industrie | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_horses_occupation_members_commerce | owners horses occupation members commerce | vieh_gross_besitzer_von_pferden_berufszugehorige_handel | Besitzer von Pferden > Berufszugehörige > Handel | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_horses_occupation_members_active_military | owners horses occupation members active military | vieh_gross_besitzer_von_pferden_berufszugehorige_aktives_militar | Besitzer von Pferden > Berufszugehörige > aktives Militär | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_horses_occupation_members_other_occupations | owners horses occupation members other occupations | vieh_gross_besitzer_von_pferden_berufszugehorige_andere_berufe | Besitzer von Pferden > Berufszugehörige > andere Berufe | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_horses_legal_persons | owners horses legal persons | vieh_gross_besitzer_von_pferden_jurist_personen | Besitzer von Pferden > jurist. Personen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_horses_total | owners horses total | vieh_gross_besitzer_von_pferden_zusammen | Besitzer von Pferden > zusammen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_solipeds_occupation_members_agriculture | owners solipeds occupation members agriculture | vieh_gross_besitzer_von_einhufern_berufszugehorige_landwirtschaft | Besitzer von Einhufern > Berufszugehörige > Landwirtschaft | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_solipeds_occupation_members_other_occupations | owners solipeds occupation members other occupations | vieh_gross_besitzer_von_einhufern_berufszugehorige_andere_berufe | Besitzer von Einhufern > Berufszugehörige > andere Berufe | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_solipeds_legal_persons | owners solipeds legal persons | vieh_gross_besitzer_von_einhufern_jurist_personen | Besitzer von Einhufern > jurist. Personen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_solipeds_total | owners solipeds total | vieh_gross_besitzer_von_einhufern_zusammen | Besitzer von Einhufern > zusammen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_cattle_occupation_members_agriculture | owners cattle occupation members agriculture | vieh_gross_besitzer_von_rindern_berufszugehorige_landwirtschaft | Besitzer von Rindern > Berufszugehörige > Landwirtschaft | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_cattle_occupation_members_industry | owners cattle occupation members industry | vieh_gross_besitzer_von_rindern_berufszugehorige_industrie | Besitzer von Rindern > Berufszugehörige > Industrie | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_cattle_occupation_members_commerce | owners cattle occupation members commerce | vieh_gross_besitzer_von_rindern_berufszugehorige_handel | Besitzer von Rindern > Berufszugehörige > Handel | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_cattle_occupation_members_other_occupations | owners cattle occupation members other occupations | vieh_gross_besitzer_von_rindern_berufszugehorige_andere_berufe | Besitzer von Rindern > Berufszugehörige > andere Berufe | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_cattle_legal_persons | owners cattle legal persons | vieh_gross_besitzer_von_rindern_jurist_personen | Besitzer von Rindern > jurist. Personen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_owners_cattle_total | owners cattle total | vieh_gross_besitzer_von_rindern_zusammen | Besitzer von Rindern > zusammen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_horses_agriculture | agricultural landowners horses agriculture | vieh_gross_landwirtschaftliche_grundbesitzer_mit_pferden_landwirtschaft | Landwirtschaftliche Grundbesitzer mit Pferden > Landwirtschaft | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_horses_other_occupations | agricultural landowners horses other occupations | vieh_gross_landwirtschaftliche_grundbesitzer_mit_pferden_andere_berufe | Landwirtschaftliche Grundbesitzer mit Pferden > andere Berufe | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_horses_legal_persons | agricultural landowners horses legal persons | vieh_gross_landwirtschaftliche_grundbesitzer_mit_pferden_jurist_personen | Landwirtschaftliche Grundbesitzer mit Pferden > jurist. Personen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_horses_total | agricultural landowners horses total | vieh_gross_landwirtschaftliche_grundbesitzer_mit_pferden_zusammen | Landwirtschaftliche Grundbesitzer mit Pferden > zusammen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_other_solipeds_agriculture | agricultural landowners other solipeds agriculture | vieh_gross_landwirtschaftliche_grundbesitzer_mit_anderen_einhufern_landwirtschaft | Landwirtschaftliche Grundbesitzer mit anderen Einhufern > Landwirtschaft | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_other_solipeds_other_occupations | agricultural landowners other solipeds other occupations | vieh_gross_landwirtschaftliche_grundbesitzer_mit_anderen_einhufern_andere_berufe | Landwirtschaftliche Grundbesitzer mit anderen Einhufern > andere Berufe | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_other_solipeds_legal_persons | agricultural landowners other solipeds legal persons | vieh_gross_landwirtschaftliche_grundbesitzer_mit_anderen_einhufern_jurist_personen | Landwirtschaftliche Grundbesitzer mit anderen Einhufern > jurist. Personen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_other_solipeds_total | agricultural landowners other solipeds total | vieh_gross_landwirtschaftliche_grundbesitzer_mit_anderen_einhufern_zusammen | Landwirtschaftliche Grundbesitzer mit anderen Einhufern > zusammen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_cattle_agriculture | agricultural landowners cattle agriculture | vieh_gross_landwirtschaftliche_grundbesitzer_mit_rindern_landwirtschaft | Landwirtschaftliche Grundbesitzer mit Rindern > Landwirtschaft | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_cattle_other_occupations | agricultural landowners cattle other occupations | vieh_gross_landwirtschaftliche_grundbesitzer_mit_rindern_andere_berufe | Landwirtschaftliche Grundbesitzer mit Rindern > andere Berufe | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_cattle_legal_persons | agricultural landowners cattle legal persons | vieh_gross_landwirtschaftliche_grundbesitzer_mit_rindern_jurist_personen | Landwirtschaftliche Grundbesitzer mit Rindern > jurist. Personen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (large) | livestock_large_agricultural_landowners_cattle_total | agricultural landowners cattle total | vieh_gross_landwirtschaftliche_grundbesitzer_mit_rindern_zusammen | Landwirtschaftliche Grundbesitzer mit Rindern > zusammen | Viehzaehlung Grossviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_goats_occupation_members_agriculture | owners goats occupation members agriculture | vieh_klein_besitzer_von_ziegen_berufszugehorige_landwirtschaft | Besitzer von Ziegen > Berufszugehörige > Landwirtschaft | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_goats_occupation_members_industry | owners goats occupation members industry | vieh_klein_besitzer_von_ziegen_berufszugehorige_industrie | Besitzer von Ziegen > Berufszugehörige > Industrie | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_goats_occupation_members_commerce | owners goats occupation members commerce | vieh_klein_besitzer_von_ziegen_berufszugehorige_handel | Besitzer von Ziegen > Berufszugehörige > Handel | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_goats_occupation_members_other_occupations | owners goats occupation members other occupations | vieh_klein_besitzer_von_ziegen_berufszugehorige_andere_berufe | Besitzer von Ziegen > Berufszugehörige > andere Berufe | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_goats_legal_persons | owners goats legal persons | vieh_klein_besitzer_von_ziegen_jurist_personen | Besitzer von Ziegen > jurist. Personen | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_goats_total | owners goats total | vieh_klein_besitzer_von_ziegen_zusammen | Besitzer von Ziegen > zusammen | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_sheep_occupation_members_agriculture | owners sheep occupation members agriculture | vieh_klein_besitzer_von_schafen_berufszugehorige_landwirtschaft | Besitzer von Schafen > Berufszugehörige > Landwirtschaft | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_sheep_other_occupations | owners sheep other occupations | vieh_klein_besitzer_von_schafen_andere_berufe | Besitzer von Schafen > andere Berufe | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_sheep_legal_persons | owners sheep legal persons | vieh_klein_besitzer_von_schafen_jurist_personen | Besitzer von Schafen > jurist. Personen | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_sheep_total | owners sheep total | vieh_klein_besitzer_von_schafen_zusammen | Besitzer von Schafen > zusammen | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_pigs_occupation_members_agriculture | owners pigs occupation members agriculture | vieh_klein_besitzer_von_schweinen_berufszugehorige_landwirtschaft | Besitzer von Schweinen > Berufszugehörige > Landwirtschaft | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_pigs_occupation_members_industry | owners pigs occupation members industry | vieh_klein_besitzer_von_schweinen_berufszugehorige_industrie | Besitzer von Schweinen > Berufszugehörige > Industrie | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_pigs_occupation_members_commerce | owners pigs occupation members commerce | vieh_klein_besitzer_von_schweinen_berufszugehorige_handel | Besitzer von Schweinen > Berufszugehörige > Handel | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_pigs_occupation_members_other_occupations | owners pigs occupation members other occupations | vieh_klein_besitzer_von_schweinen_berufszugehorige_andere_berufe | Besitzer von Schweinen > Berufszugehörige > andere Berufe | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_pigs_legal_persons | owners pigs legal persons | vieh_klein_besitzer_von_schweinen_jurist_personen | Besitzer von Schweinen > jurist. Personen | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_owners_pigs_total | owners pigs total | vieh_klein_besitzer_von_schweinen_zusammen | Besitzer von Schweinen > zusammen | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_goats_agriculture | agricultural landowners holdings goats agriculture | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_ziegen_landwirtschaft | Landwirtschaftliche Grundbesitzer im Besitze von > Ziegen > Landwirtschaft | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_goats_other_occupations | agricultural landowners holdings goats other occupations | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_ziegen_andere_berufe | Landwirtschaftliche Grundbesitzer im Besitze von > Ziegen > andere Berufe | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_goats_total | agricultural landowners holdings goats total | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_ziegen_zusammen | Landwirtschaftliche Grundbesitzer im Besitze von > Ziegen > zusammen | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_sheep_agriculture | agricultural landowners holdings sheep agriculture | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schafen_landwirtschaft | Landwirtschaftliche Grundbesitzer im Besitze von > Schafen > Landwirtschaft | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_sheep_other_occupations | agricultural landowners holdings sheep other occupations | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schafen_andere_berufe | Landwirtschaftliche Grundbesitzer im Besitze von > Schafen > andere Berufe | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_sheep_legal_persons | agricultural landowners holdings sheep legal persons | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schafen_jurist_personen | Landwirtschaftliche Grundbesitzer im Besitze von > Schafen > jurist. Personen | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_sheep_total | agricultural landowners holdings sheep total | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schafen_zusammen | Landwirtschaftliche Grundbesitzer im Besitze von > Schafen > zusammen | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_pigs_agriculture | agricultural landowners holdings pigs agriculture | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_landwirtschaft | Landwirtschaftliche Grundbesitzer im Besitze von > Schweinen > Landwirtschaft | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_pigs_industry | agricultural landowners holdings pigs industry | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_industrie | Landwirtschaftliche Grundbesitzer im Besitze von > Schweinen > Industrie | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_pigs_commerce | agricultural landowners holdings pigs commerce | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_handel | Landwirtschaftliche Grundbesitzer im Besitze von > Schweinen > Handel | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_pigs_other_occupations | agricultural landowners holdings pigs other occupations | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_andere_berufe | Landwirtschaftliche Grundbesitzer im Besitze von > Schweinen > andere Berufe | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_pigs_legal_persons | agricultural landowners holdings pigs legal persons | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_jurist_personen | Landwirtschaftliche Grundbesitzer im Besitze von > Schweinen > jurist. Personen | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (small) | livestock_small_agricultural_landowners_holdings_pigs_total | agricultural landowners holdings pigs total | vieh_klein_landwirtschaftliche_grundbesitzer_im_besitze_von_schweinen_zusemmen | Landwirtschaftliche Grundbesitzer im Besitze von > Schweinen > zusemmen | Viehzaehlung Kleinviehbesitzer 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_total_number_livestock_owners | total number livestock owners | vieh_ges_gesamtzahl_der_viehbesitzer | Gesamtzahl der Viehbesitzer | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_held_horses | held horses | vieh_ges_davon_besitzen_pferde | Davon besitzen > Pferde | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_held_other_solipeds | held other solipeds | vieh_ges_davon_besitzen_andere_einhufer | Davon besitzen > andere Einhufer | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_held_cattle | held cattle | vieh_ges_davon_besitzen_rinder | Davon besitzen > Rinder | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_held_small_livestock | held small livestock | vieh_ges_davon_besitzen_kleinvieh | Davon besitzen > Kleinvieh | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_held_beehives | held beehives | vieh_ges_davon_besitzen_bienen | Davon besitzen > Bienen | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_horses_alone | under total number owners horses alone | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_pferden_allein | Unter der Gesamtzahl sind Besitzer von > Pferden allein | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_horses_cattle_alone | under total number owners horses cattle alone | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_pferden_und_rindern_allein | Unter der Gesamtzahl sind Besitzer von > Pferden und Rindern allein | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_horses_cattle_small_livestock | under total number owners horses cattle small livestock | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_pferden_rindern_und_kleinvieh | Unter der Gesamtzahl sind Besitzer von > Pferden, Rindern und Kleinvieh | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_cattle_alone | under total number owners cattle alone | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_rindern_allein | Unter der Gesamtzahl sind Besitzer von > Rindern allein | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_cattle_small_livestock_without_horses | under total number owners cattle small livestock without horses | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_rindern_und_kleinvieh_ohne_pferde | Unter der Gesamtzahl sind Besitzer von > Rindern und Kleinvieh ohne Pferde | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_goats_adjacent_cattle | under total number owners goats adjacent cattle | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_ziegen_neben_rindern | Unter der Gesamtzahl sind Besitzer von > Ziegen neben Rindern | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_goats_without_cattle | under total number owners goats without cattle | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_ziegen_ohne_rinder | Unter der Gesamtzahl sind Besitzer von > Ziegen ohne Rinder | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_goats_alone | under total number owners goats alone | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_ziegen_allein | Unter der Gesamtzahl sind Besitzer von > Ziegen allein | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_sheep_adjacent_cattle | under total number owners sheep adjacent cattle | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_schafen_neben_rindern | Unter der Gesamtzahl sind Besitzer von > Schafen neben Rindern | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_sheep_without_cattle | under total number owners sheep without cattle | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_schafen_ohne_rinder | Unter der Gesamtzahl sind Besitzer von > Schafen ohne Rinder | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_pigs_cattle_without_sheep_goats | under total number owners pigs cattle without sheep goats | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_schweinen_mit_rindern_ohne_schafe_oder_ziegen | Unter der Gesamtzahl sind Besitzer von > Schweinen mit Rindern ohne Schafe oder Ziegen | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Livestock (total) | livestock_total_under_total_number_owners_pigs_cattle_sheep_goats | under total number owners pigs cattle sheep goats | vieh_ges_unter_der_gesamtzahl_sind_besitzer_von_schweinen_mit_rindern_mit_schafe_oder_ziegen | Unter der Gesamtzahl sind Besitzer von > Schweinen mit Rindern mit Schafe oder Ziegen | Viehzaehlung Viehbesitzer Gesamtzahl 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_economically_active_permanent | occupation class a land forestry byproducts economically active permanent | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_selb_standige | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > Selb-ständige | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_economically_active_tenants | occupation class a land forestry byproducts economically active tenants | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_pachter | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > Pächter | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_economically_active_employees | occupation class a land forestry byproducts economically active employees | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_angestellte | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > Angestellte | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_economically_active_workers | occupation class a land forestry byproducts economically active workers | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_arbeiter | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > Arbeiter | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_economically_active_apprentices | occupation class a land forestry byproducts economically active apprentices | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_lehrlinge | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > Lehrlinge | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_economically_active_day_laborers | occupation class a land forestry byproducts economically active day laborers | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_taglohner | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > Taglöhner | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_economically_active_assisting_family_members | occupation class a land forestry byproducts economically active assisting family members | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_helfende_familien_mitgl | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > helfende Familien-mitgl. | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_economically_active_total | occupation class a land forestry byproducts economically active total | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_berufstatige_zusammen | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Berufstätige > zusammen | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_members_without_own_main_occupation | occupation class a land forestry byproducts members without own main occupation | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_angehorige_ohne_eigenen_hauptberuf | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Angehörige ohne eigenen Hauptberuf | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_house_servants_servants | occupation class a land forestry byproducts house servants servants | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hausdiener_schaft_dienstboten | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hausdiener-schaft, Dienstboten | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_sum_occupation | occupation class a land forestry byproducts sum occupation | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_summe_der_berufs_zugehorigen | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Summe der Berufs-zugehörigen | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_secondary_occupation_classes_a | occupation class a land forestry byproducts secondary occupation classes a | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hiervon_mit_nebenerwerb_in_den_klassen_a | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hiervon mit Nebenerwerb > in den Klassen > A | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_secondary_occupation_classes_b | occupation class a land forestry byproducts secondary occupation classes b | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hiervon_mit_nebenerwerb_in_den_klassen_b | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hiervon mit Nebenerwerb > in den Klassen > B | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_secondary_occupation_classes_c | occupation class a land forestry byproducts secondary occupation classes c | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hiervon_mit_nebenerwerb_in_den_klassen_c | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hiervon mit Nebenerwerb > in den Klassen > C | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_secondary_occupation_classes_d | occupation class a land forestry byproducts secondary occupation classes d | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hiervon_mit_nebenerwerb_in_den_klassen_d | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hiervon mit Nebenerwerb > in den Klassen > D | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_a_land_forestry_byproducts_secondary_occupation_overall | occupation class a land forestry byproducts secondary occupation overall | beruf_berufsklasse_a_land_und_forstwirtschaft_und_deren_nebennutzungen_hiervon_mit_nebenerwerb_uberhaupt | Berufsklasse A. Land- und Forstwirtschaft und deren Nebennutzungen > Hiervon mit Nebenerwerb > überhaupt | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_economically_active_permanent | occupation class b industry trades economically active permanent | beruf_berufsklasse_b_industrie_und_gewerbe_berufstatige_selb_standige | Berufsklasse B. Industrie und Gewerbe > Berufstätige > Selb-ständige | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_economically_active_tenants | occupation class b industry trades economically active tenants | beruf_berufsklasse_b_industrie_und_gewerbe_berufstatige_pachter | Berufsklasse B. Industrie und Gewerbe > Berufstätige > Pächter | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_economically_active_employees | occupation class b industry trades economically active employees | beruf_berufsklasse_b_industrie_und_gewerbe_berufstatige_angestellte | Berufsklasse B. Industrie und Gewerbe > Berufstätige > Angestellte | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_economically_active_workers | occupation class b industry trades economically active workers | beruf_berufsklasse_b_industrie_und_gewerbe_berufstatige_arbeiter | Berufsklasse B. Industrie und Gewerbe > Berufstätige > Arbeiter | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_economically_active_apprentices | occupation class b industry trades economically active apprentices | beruf_berufsklasse_b_industrie_und_gewerbe_berufstatige_lehrlinge | Berufsklasse B. Industrie und Gewerbe > Berufstätige > Lehrlinge | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_economically_active_day_laborers | occupation class b industry trades economically active day laborers | beruf_berufsklasse_b_industrie_und_gewerbe_berufstatige_taglohner | Berufsklasse B. Industrie und Gewerbe > Berufstätige > Taglöhner | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_economically_active_assisting_family_members | occupation class b industry trades economically active assisting family members | beruf_berufsklasse_b_industrie_und_gewerbe_berufstatige_helfende_familien_mitgl | Berufsklasse B. Industrie und Gewerbe > Berufstätige > helfende Familien-mitgl. | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_economically_active_total | occupation class b industry trades economically active total | beruf_berufsklasse_b_industrie_und_gewerbe_berufstatige_zusammen | Berufsklasse B. Industrie und Gewerbe > Berufstätige > zusammen | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_members_without_own_main_occupation | occupation class b industry trades members without own main occupation | beruf_berufsklasse_b_industrie_und_gewerbe_angehorige_ohne_eigenen_hauptberuf | Berufsklasse B. Industrie und Gewerbe > Angehörige ohne eigenen Hauptberuf | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_house_servants_servants | occupation class b industry trades house servants servants | beruf_berufsklasse_b_industrie_und_gewerbe_hausdiener_schaft_dienstboten | Berufsklasse B. Industrie und Gewerbe > Hausdiener-schaft, Dienstboten | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_sum_occupation | occupation class b industry trades sum occupation | beruf_berufsklasse_b_industrie_und_gewerbe_summe_der_berufs_zugehorigen | Berufsklasse B. Industrie und Gewerbe > Summe der Berufs-zugehörigen | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_secondary_occupation_classes_a | occupation class b industry trades secondary occupation classes a | beruf_berufsklasse_b_industrie_und_gewerbe_hiervon_mit_nebenerwerb_in_den_klassen_a | Berufsklasse B. Industrie und Gewerbe > Hiervon mit Nebenerwerb > in den Klassen > A | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_secondary_occupation_classes_b | occupation class b industry trades secondary occupation classes b | beruf_berufsklasse_b_industrie_und_gewerbe_hiervon_mit_nebenerwerb_in_den_klassen_b | Berufsklasse B. Industrie und Gewerbe > Hiervon mit Nebenerwerb > in den Klassen > B | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_secondary_occupation_classes_c | occupation class b industry trades secondary occupation classes c | beruf_berufsklasse_b_industrie_und_gewerbe_hiervon_mit_nebenerwerb_in_den_klassen_c | Berufsklasse B. Industrie und Gewerbe > Hiervon mit Nebenerwerb > in den Klassen > C | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_secondary_occupation_classes_d | occupation class b industry trades secondary occupation classes d | beruf_berufsklasse_b_industrie_und_gewerbe_hiervon_mit_nebenerwerb_in_den_klassen_d | Berufsklasse B. Industrie und Gewerbe > Hiervon mit Nebenerwerb > in den Klassen > D | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_b_industry_trades_secondary_occupation_overall | occupation class b industry trades secondary occupation overall | beruf_berufsklasse_b_industrie_und_gewerbe_hiervon_mit_nebenerwerb_uberhaupt | Berufsklasse B. Industrie und Gewerbe > Hiervon mit Nebenerwerb > überhaupt | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_permanent | occupation class c commerce transport including inn tavern economically active permanent | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_selb_standige | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > Selb-ständige | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_tenants | occupation class c commerce transport including inn tavern economically active tenants | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_pachter | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > Pächter | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_employees | occupation class c commerce transport including inn tavern economically active employees | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_angestellte | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > Angestellte | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_workers | occupation class c commerce transport including inn tavern economically active workers | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_arbeiter | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > Arbeiter | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_apprentices | occupation class c commerce transport including inn tavern economically active apprentices | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_lehrlinge | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > Lehrlinge | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_day_laborers | occupation class c commerce transport including inn tavern economically active day laborers | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_taglohner | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > Taglöhner | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_assisting_family_members | occupation class c commerce transport including inn tavern economically active assisting family members | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_helfende_familien_mitgl | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > helfende Familien-mitgl. | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_economically_active_total | occupation class c commerce transport including inn tavern economically active total | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_berufstatige_zusammen | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Berufstätige > zusammen | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_members_without_own_main_occupation | occupation class c commerce transport including inn tavern members without own main occupation | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_angehorige_ohne_eigenen_hauptberuf | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Angehörige ohne eigenen Hauptberuf | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_house_servants_servants | occupation class c commerce transport including inn tavern house servants servants | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hausdiener_schaft_dienstboten | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hausdiener-schaft, Dienstboten | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_sum_occupation | occupation class c commerce transport including inn tavern sum occupation | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_summe_der_berufs_zugehorigen | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Summe der Berufs-zugehörigen | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_secondary_occupation_classes_a | occupation class c commerce transport including inn tavern secondary occupation classes a | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hiervon_mit_nebenerwerb_in_den_klassen_a | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hiervon mit Nebenerwerb > in den Klassen > A | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_secondary_occupation_classes_b | occupation class c commerce transport including inn tavern secondary occupation classes b | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hiervon_mit_nebenerwerb_in_den_klassen_b | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hiervon mit Nebenerwerb > in den Klassen > B | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_secondary_occupation_classes_c | occupation class c commerce transport including inn tavern secondary occupation classes c | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hiervon_mit_nebenerwerb_in_den_klassen_c | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hiervon mit Nebenerwerb > in den Klassen > C | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_secondary_occupation_classes_d | occupation class c commerce transport including inn tavern secondary occupation classes d | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hiervon_mit_nebenerwerb_in_den_klassen_d | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hiervon mit Nebenerwerb > in den Klassen > D | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_c_commerce_transport_including_inn_tavern_secondary_occupation_overall | occupation class c commerce transport including inn tavern secondary occupation overall | beruf_berufsklasse_c_handel_und_verkehr_einschliesslich_der_gast_und_schankwirtschaft_hiervon_mit_nebenerwerb_uberhaupt | Berufsklasse C. Handel und Verkehr einschließlich der Gast- und Schankwirtschaft > Hiervon mit Nebenerwerb > überhaupt | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_permanent | occupation class d public military service liberal occupations without occupation economically active permanent | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_selb_standige | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > Selb-ständige | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_tenants | occupation class d public military service liberal occupations without occupation economically active tenants | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_pachter | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > Pächter | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_employees | occupation class d public military service liberal occupations without occupation economically active employees | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_angestellte | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > Angestellte | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_workers | occupation class d public military service liberal occupations without occupation economically active workers | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_arbeiter | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > Arbeiter | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_apprentices | occupation class d public military service liberal occupations without occupation economically active apprentices | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_lehrlinge | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > Lehrlinge | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_day_laborers | occupation class d public military service liberal occupations without occupation economically active day laborers | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_taglohner | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > Taglöhner | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_assisting_family_members | occupation class d public military service liberal occupations without occupation economically active assisting family members | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_helfende_familien_mitgl | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > helfende Familien-mitgl. | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_economically_active_total | occupation class d public military service liberal occupations without occupation economically active total | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_berufstatige_zusammen | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Berufstätige > zusammen | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_members_without_own_main_occupation | occupation class d public military service liberal occupations without occupation members without own main occupation | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_angehorige_ohne_eigenen_hauptberuf | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Angehörige ohne eigenen Hauptberuf | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_house_servants_servants | occupation class d public military service liberal occupations without occupation house servants servants | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hausdiener_schaft_dienstboten | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hausdiener-schaft, Dienstboten | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_sum_occupation | occupation class d public military service liberal occupations without occupation sum occupation | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_summe_der_berufs_zugehorigen | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Summe der Berufs-zugehörigen | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_secondary_occupation_classes_a | occupation class d public military service liberal occupations without occupation secondary occupation classes a | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hiervon_mit_nebenerwerb_in_den_klassen_a | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hiervon mit Nebenerwerb > in den Klassen > A | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_secondary_occupation_classes_b | occupation class d public military service liberal occupations without occupation secondary occupation classes b | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hiervon_mit_nebenerwerb_in_den_klassen_b | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hiervon mit Nebenerwerb > in den Klassen > B | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_secondary_occupation_classes_c | occupation class d public military service liberal occupations without occupation secondary occupation classes c | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hiervon_mit_nebenerwerb_in_den_klassen_c | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hiervon mit Nebenerwerb > in den Klassen > C | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_secondary_occupation_classes_d | occupation class d public military service liberal occupations without occupation secondary occupation classes d | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hiervon_mit_nebenerwerb_in_den_klassen_d | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hiervon mit Nebenerwerb > in den Klassen > D | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Occupation | occ_occupation_class_d_public_military_service_liberal_occupations_without_occupation_secondary_occupation_overall | occupation class d public military service liberal occupations without occupation secondary occupation overall | beruf_berufsklasse_d_offentlicher_und_militardienst_freie_berufe_berufslose_hiervon_mit_nebenerwerb_uberhaupt | Berufsklasse D. Öffentlicher und Militärdienst, freie Berufe, Berufslose > Hiervon mit Nebenerwerb > überhaupt | Volkszaehlung Beruf 1910 Political district 406.xlsx |
| cis | Literacy / education | lit_population_present | total present population | bild_die_gesamte_anwesande_bevolkerung | Die gesamte anwesande Bevölkerung | Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx |
| cis | Literacy / education | lit_age_0_10 | age years to including 10 birth year 1910 1900 | bild_davon_standen_im_alter_von_vollen_jahren_bis_einschlieblich_10_geburtsjahre_1910_1900 | Davon standen im Alter von vollen Jahren > bis einschlieblich 10 (Geburtsjahre 1910-1900) | Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx |
| cis | Literacy / education | lit_age_over10 | age years over 10 birth year before 1900 | bild_davon_standen_im_alter_von_vollen_jahren_uber_10_geburtsjahre_vor_1900 | Davon standen im Alter von vollen Jahren > über 10 (Geburtsjahre vor 1900) | Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx |
| cis | Literacy / education | lit_read_write | over 10 years age persons read write | bild_von_den_uber_10_jahre_alten_personen_konnen_lesen_und_schreiben | Von den über 10 Jahre alten Personen können > lesen und schreiben | Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx |
| cis | Literacy / education | lit_read_only | over 10 years age persons read | bild_von_den_uber_10_jahre_alten_personen_konnen_nur_lesen | Von den über 10 Jahre alten Personen können > nur lesen | Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx |
| cis | Literacy / education | lit_illiterate | over 10 years age persons neither read nor write | bild_von_den_uber_10_jahre_alten_personen_konnen_weder_lesen_noch_schreiben | Von den über 10 Jahre alten Personen können > weder lesen noch schreiben | Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx |
| cis | Literacy / education | disab_blind | blind total | bild_blind_zus | blind zus. | Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx |
| cis | Literacy / education | disab_deafmute | deafmute total | bild_taubstumm_zus | taubstumm zus. | Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx |
| cis | Literacy / education | disab_blind_deafmute | blind deafmute total | bild_blind_und_taubstumm_zus | blind und taubstumm zus. | Volkszaehlung Bildungsgrad 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_lt500_localities | under 500 inhabitants number localities | pop_unter_500_einwohner_azahl_der_ortschaften | Unter 500 Einwohner > Azahl der Ortschaften | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_lt500_houses_inhabited | under 500 inhabitants inhabited houses | pop_unter_500_einwohner_bewohnte_hauser | Unter 500 Einwohner > bewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_lt500_houses_uninhabited | under 500 inhabitants uninhabited houses | pop_unter_500_einwohner_unbewohnte_hauser | Unter 500 Einwohner > unbewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_lt500_households | under 500 inhabitants households | pop_unter_500_einwohner_wohnparteien | Unter 500 Einwohner > Wohnparteien | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_lt500_inhabitants | under 500 inhabitants | pop_unter_500_einwohner_einwohner | Unter 500 Einwohner > Einwohner | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_500_2000_localities | 500 to 2000 inhabitants number localities | pop_500_bis_2000_einwohner_azahl_der_ortschaften | 500 bis 2000 Einwohner > Azahl der Ortschaften | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_500_2000_houses_inhabited | 500 to 2000 inhabitants inhabited houses | pop_500_bis_2000_einwohner_bewohnte_hauser | 500 bis 2000 Einwohner > bewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_500_2000_houses_uninhabited | 500 to 2000 inhabitants uninhabited houses | pop_500_bis_2000_einwohner_unbewohnte_hauser | 500 bis 2000 Einwohner > unbewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_500_2000_households | 500 to 2000 inhabitants households | pop_500_bis_2000_einwohner_wohnparteien | 500 bis 2000 Einwohner > Wohnparteien | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_500_2000_inhabitants | 500 to 2000 inhabitants | pop_500_bis_2000_einwohner_einwohner | 500 bis 2000 Einwohner > Einwohner | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_2001_5000_localities | 2001 to 5000 inhabitants number localities | pop_2001_bis_5000_einwohner_azahl_der_ortschaften | 2001 bis 5000 Einwohner > Azahl der Ortschaften | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_2001_5000_houses_inhabited | 2001 to 5000 inhabitants inhabited houses | pop_2001_bis_5000_einwohner_bewohnte_hauser | 2001 bis 5000 Einwohner > bewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_2001_5000_houses_uninhabited | 2001 to 5000 inhabitants uninhabited houses | pop_2001_bis_5000_einwohner_unbewohnte_hauser | 2001 bis 5000 Einwohner > unbewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_2001_5000_households | 2001 to 5000 inhabitants households | pop_2001_bis_5000_einwohner_wohnparteien | 2001 bis 5000 Einwohner > Wohnparteien | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_2001_5000_inhabitants | 2001 to 5000 inhabitants | pop_2001_bis_5000_einwohner_einwohner | 2001 bis 5000 Einwohner > Einwohner | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_5001_10000_localities | 5001 to 10000 inhabitants number localities | pop_5001_bis_10000_einwohner_azahl_der_ortschaften | 5001 bis 10000 Einwohner > Azahl der Ortschaften | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_5001_10000_houses_inhabited | 5001 to 10000 inhabitants inhabited houses | pop_5001_bis_10000_einwohner_bewohnte_hauser | 5001 bis 10000 Einwohner > bewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_5001_10000_houses_uninhabited | 5001 to 10000 inhabitants uninhabited houses | pop_5001_bis_10000_einwohner_unbewohnte_hauser | 5001 bis 10000 Einwohner > unbewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_5001_10000_households | 5001 to 10000 inhabitants households | pop_5001_bis_10000_einwohner_wohnparteien | 5001 bis 10000 Einwohner > Wohnparteien | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_5001_10000_inhabitants | 5001 to 10000 inhabitants | pop_5001_bis_10000_einwohner_einwohner | 5001 bis 10000 Einwohner > Einwohner | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_10001_20000_localities | 10001 to 20000 inhabitants number localities | pop_10001_bis_20000_einwohner_azahl_der_ortschaften | 10001 bis 20000 Einwohner > Azahl der Ortschaften | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_10001_20000_houses_inhabited | 10001 to 20000 inhabitants inhabited houses | pop_10001_bis_20000_einwohner_bewohnte_hauser | 10001 bis 20000 Einwohner > bewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_10001_20000_houses_uninhabited | 10001 to 20000 inhabitants uninhabited houses | pop_10001_bis_20000_einwohner_unbewohnte_hauser | 10001 bis 20000 Einwohner > unbewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_10001_20000_households | 10001 to 20000 inhabitants households | pop_10001_bis_20000_einwohner_wohnparteien | 10001 bis 20000 Einwohner > Wohnparteien | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_10001_20000_inhabitants | 10001 to 20000 inhabitants | pop_10001_bis_20000_einwohner_einwohner | 10001 bis 20000 Einwohner > Einwohner | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_gt20000_localities | over 20000 inhabitants number localities | pop_uber_20000_einwohner_azahl_der_ortschaften | Über 20000 Einwohner > Azahl der Ortschaften | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_gt20000_houses_inhabited | over 20000 inhabitants inhabited houses | pop_uber_20000_einwohner_bewohnte_hauser | Über 20000 Einwohner > bewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_gt20000_houses_uninhabited | over 20000 inhabitants uninhabited houses | pop_uber_20000_einwohner_unbewohnte_hauser | Über 20000 Einwohner > unbewohnte > Häuser | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_gt20000_households | over 20000 inhabitants households | pop_uber_20000_einwohner_wohnparteien | Über 20000 Einwohner > Wohnparteien | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Population by settlement size | pop_gt20000_inhabitants | over 20000 inhabitants | pop_uber_20000_einwohner_einwohner | Über 20000 Einwohner > Einwohner | Volkszaehlung Einwohner nach Größenkategorien 1910 Political district 406.xlsx |
| cis | Religion | rel_roman_catholic | present population by religion roman catholic | rel_romisch_katholisch | Anwesende Bevölkerung nach der Religion > römisch- > katholisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_greek_catholic | present population by religion greek catholic | rel_griechisch_katholisch | Anwesende Bevölkerung nach der Religion > griechisch- > katholisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_armenian_catholic | present population by religion armenian catholic | rel_armenisch_katholisch | Anwesende Bevölkerung nach der Religion > armenisch- > katholisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_old_catholic | present population by religion age catholic | rel_alt_katholisch | Anwesende Bevölkerung nach der Religion > alt- > katholisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_greek_orthodox | present population by religion greek orthodox | rel_griechisch_orientalisch | Anwesende Bevölkerung nach der Religion > griechisch- > orientalisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_armenian_orthodox | present population by religion armenian orthodox | rel_armenisch_orientalisch | Anwesende Bevölkerung nach der Religion > armenisch- > orientalisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_lutheran | present population by religion evangelical augsburg confession | rel_evangelisch_augsburger_bekenntnisses | Anwesende Bevölkerung nach der Religion > evangelisch > Augsburger Bekenntnisses | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_calvinist | present population by religion evangelical helvetic confession | rel_evangelisch_helvetischen_bekenntnisses | Anwesende Bevölkerung nach der Religion > evangelisch > helvetischen Bekenntnisses | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_moravian | present population by religion moravian | rel_herrnhuter | Anwesende Bevölkerung nach der Religion > Herrnhuter | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_anglican | present population by religion anglican | rel_anglikaner | Anwesende Bevölkerung nach der Religion > Anglikaner | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_mennonite | present population by religion mennonite | rel_mennoniten | Anwesende Bevölkerung nach der Religion > Mennoniten | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_unitarian | present population by religion unitarian | rel_unitarier | Anwesende Bevölkerung nach der Religion > Unitarier | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_lipovan | present population by religion lipovan | rel_lippowaner | Anwesende Bevölkerung nach der Religion > Lippowaner | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_jewish | present population by religion jewish | rel_israeliten | Anwesende Bevölkerung nach der Religion > Israeliten | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_muslim | present population by religion muslim | rel_islamiten | Anwesende Bevölkerung nach der Religion > Islamiten | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_other | present population by religion other confessions | rel_andere_konfessionen | Anwesende Bevölkerung nach der Religion > andere Konfessionen | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_none | present population by religion no religion | rel_konfessionslose | Anwesende Bevölkerung nach der Religion > Konfessionslose | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Religion | rel_total | present population by religion total 2 18 | rel_zusammen | Anwesende Bevölkerung nach der Religion > zusammen (Spalte 2-18) | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Colloquial language (ethnicity) | lang_german | present population austrian citizenship by colloquial language german | umgspr_deutsch | Anwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > deutsch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Colloquial language (ethnicity) | lang_czech | present population austrian citizenship by colloquial language czech moravian slovak | umgspr_bohmisch_mahrisch_slowakisch | Anwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > böhmisch-mährisch -slowakisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Colloquial language (ethnicity) | lang_polish | present population austrian citizenship by colloquial language polish | umgspr_polnisch | Anwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > polnisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Colloquial language (ethnicity) | lang_ruthenian | present population austrian citizenship by colloquial language ruthenian | umgspr_ruthenisch | Anwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > ruthenisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Colloquial language (ethnicity) | lang_slovene | present population austrian citizenship by colloquial language slovene | umgspr_slowenisch | Anwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > slowenisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Colloquial language (ethnicity) | lang_serbocroat | present population austrian citizenship by colloquial language serbian croatian | umgspr_serbisch_kroatisch | Anwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > serbisch-kroatisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Colloquial language (ethnicity) | lang_italian_ladin | present population austrian citizenship by colloquial language italian ladin | umgspr_italienisch_ladinisch | Anwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > italienisch-ladinisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Colloquial language (ethnicity) | lang_romanian | present population austrian citizenship by colloquial language romanian | umgspr_rumanisch | Anwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > rumänisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Colloquial language (ethnicity) | lang_magyar | present population austrian citizenship by colloquial language magyar | umgspr_magyarisch | Anwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > magyarisch | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Colloquial language (ethnicity) | lang_total | present population austrian citizenship by colloquial language total 20 28 | umgspr_zusammen | Anwesende Bevölkerung österreichischer Staatsbürgerschaft nach der Umgangssprache > zusammen (Spalte 20-28) | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Colloquial language (ethnicity) | foreign_nationals_present | present foreign nationals | umgspr_anwesende_staatsfremde | Anwesende Staatsfremde | Volkszaehlung Religion Umgangssprache 1910 District courts 934.xlsx |
| cis | Derived share/rate | lang_*_share | Share of population by colloquial language (of lang_total) | (derived) | ||
| cis | Derived share/rate | rel_*_share | Share of population by religion (of rel_total) | (derived) | ||
| cis | Derived share/rate | literacy_rate | Literate (read+write) / population over age 10 | (derived) | ||
| cis | Derived share/rate | illegitimacy_rate | Illegitimate live births / all live births | (derived) | ||
| cis | Derived share/rate | pop_total | Total population (sum of size-class inhabitants) | (derived) | ||
| hu | Mother tongue & religion | area_cadastral_holds | area holds | etn_terulet_kat_hold | etn_terulet_kat_hold | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue & religion | pop_present | present population | etn_jelenlevo_nepesseg | etn_jelenlevo_nepesseg | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_magyar | magyar mother tongue | etn_magyar_anyanyelv | etn_magyar_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_german | german mother tongue | etn_nemet_anyanyelv | etn_nemet_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_slovak | slovak mother tongue | etn_tot_anyanyelv | etn_tot_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_romanian | romanian mother tongue | etn_olah_anyanyelv | etn_olah_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_ruthenian | ruthenian mother tongue | etn_ruthen_anyanyelv | etn_ruthen_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_croatian | croatian mother tongue | etn_horvat_anyanyelv | etn_horvat_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_serbian | serbian mother tongue | etn_szerb_anyanyelv | etn_szerb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_other | other mother tongue | etn_egyeb_anyanyelv | etn_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Religion | rel_roman_catholic | roman religion | etn_romai_kat_vallas | etn_romai_kat_vallas | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Religion | rel_greek_catholic | greek religion | etn_gorog_kat_vallas | etn_gorog_kat_vallas | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Religion | rel_calvinist | calvinist religion | etn_reformatus_vallas | etn_reformatus_vallas | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Religion | rel_lutheran | lutheran religion | etn_evangelikus_vallas | etn_evangelikus_vallas | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Religion | rel_orthodox | orthodox religion | etn_ortodox_vallas | etn_ortodox_vallas | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Religion | rel_unitarian | unitarian religion | etn_unitarius_vallas | etn_unitarius_vallas | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Religion | rel_jewish | jewish religion | etn_izraelita_vallas | etn_izraelita_vallas | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Religion | rel_other | other religion | etn_egyeb_vallas | etn_egyeb_vallas | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_bulgarian | bulgarian other mother tongue | etn_bolgar_egyeb_anyanyelv | etn_bolgar_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_bunjevac | bunjevac other mother tongue | etn_bunyevac_egyeb_anyanyelv | etn_bunyevac_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_romani | romani other mother tongue | etn_cigany_egyeb_anyanyelv | etn_cigany_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_czech_moravian | czech moravian other mother tongue | etn_cseh_morva_egyeb_anyanyelv | etn_cseh_morva_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_illyrian_dalmatian | illyrian dalmatian other mother tongue | etn_illir_dalmat_egyeb_anyanyelv | etn_illir_dalmat_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_krashovani | krashovani other mother tongue | etn_krassovan_egyeb_anyanyelv | etn_krassovan_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_polish | polish other mother tongue | etn_lengyel_egyeb_anyanyelv | etn_lengyel_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_italian | italian other mother tongue | etn_olasz_egyeb_anyanyelv | etn_olasz_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_sokci | sokci other mother tongue | etn_sokac_egyeb_anyanyelv | etn_sokac_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_slovene | slovene other mother tongue | etn_szloven_egyeb_anyanyelv | etn_szloven_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Mother tongue (ethnicity) | lang_other_other | other mother tongue | etn_egyeb_egyeb_anyanyelv | etn_egyeb_egyeb_anyanyelv | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_arable_holds | arable holds | agr_a1 szántó kat hold | agr_a1 szántó kat hold | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_arable_income | arable income | agr_a2 szántó jövedelme, korona | agr_a2 szántó jövedelme, korona | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_meadow_holds | meadow holds | agr_a5 ret kat hold | agr_a5 ret kat hold | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_meadow_income | meadow joved | agr_a6 ret jöved korona | agr_a6 ret jöved korona | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_vineyard_holds | vineyard holds | agr_a7 szőlö kat hold | agr_a7 szőlö kat hold | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_vineyard_income | vineyard joved | agr_a8 szőlő jöved korona | agr_a8 szőlő jöved korona | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_pasture_holds | pasture holds | agr_a9 legelő kat hold | agr_a9 legelő kat hold | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_pasture_income | pasture joved | agr_a0 legel jöved korona | agr_a0 legel jöved korona | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_forest_holds | forest holds | agr_b1 erdő kat hold | agr_b1 erdő kat hold | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_forest_income | erdojoved | agr_b2 erdőjöved korona | agr_b2 erdőjöved korona | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_settlement_productive_area_holds | telepul termo terulete holds | agr_b3 települ termő területe, kat hold | agr_b3 települ termő területe, kat hold | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_settlement_unproductive_area_holds | settlement termeketlen terulete holds | agr_b4 telep terméketlen területe, kat hold | agr_b4 telep terméketlen területe, kat hold | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_settlement_net_income | settlement net income | agr_b5 telep tiszta jövedelme, korona | agr_b5 telep tiszta jövedelme, korona | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_parcels_count | foldreszletek szama | agr_b6 földrészletek száma | agr_b6 földrészletek száma | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_holdings_count | birtokivek szama | agr_b7 birtokívek száma | agr_b7 birtokívek száma | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_field22 | mezo22 | agr_b8 Mező22 | agr_b8 Mező22 | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_field23 | mezo23 | agr_b9 Mező23 | agr_b9 Mező23 | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_useful_pct | useful pct | agr_b0 hasznos % | agr_b0 hasznos % | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_income_per_area_unit | jovedelem teruletegysegre | agr_c1 jövedelem területegységre | agr_c1 jövedelem területegységre | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_forest_income_per_area_unit | erdojovedelem teruletegysegre | agr_c2 erdojövedelem területegységre | agr_c2 erdojövedelem területegységre | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_vineyard_income_per_area_unit | vineyard jovedelem teruletegysegre | agr_c3 szölö jövedelem területegységre | agr_c3 szölö jövedelem területegységre | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_meadow_pasture_income_per_area_unit | meadow pasture income teruletegysegre | agr_c4 rét és legelő jövedelme területegységre | agr_c4 rét és legelő jövedelme területegységre | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_arable_income_per_area_unit | arable income teruletegysegre | agr_c5 szanto jövedelme területegysegre | agr_c5 szanto jövedelme területegysegre | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_pasture_income_2 | pasture income | agr_c6 legelö jovedelme | agr_c6 legelö jovedelme | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_meadow_income_2 | meadow income | agr_c7 rét jövedelme | agr_c7 rét jövedelme | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_income_holding | jovedelem holding | agr_c8 jövedelem per birtok | agr_c8 jövedelem per birtok | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_parcel_holding | parcel holding | agr_c9 parcella per birtok | agr_c9 parcella per birtok | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_parcel_size | parcellameret | agr_c0 parcellaméret | agr_c0 parcellaméret | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_farm_size | gazdasagmeret | agr_d1 gazdaságméret | agr_d1 gazdaságméret | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_per_capita_income | per capita jovedelem | agr_d2 egy lakosra juto jövedelem | agr_d2 egy lakosra juto jövedelem | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_per_capita_area | per capita area | agr_d3 egy lakosra juto terület | agr_d3 egy lakosra juto terület | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_forest_pct | forest pct | agr_d4 erdo% | agr_d4 erdo% | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_vineyard_pct | vineyard pct | agr_d5 szolo% | agr_d5 szolo% | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_meadow_pasture_pct | meadow pasture pct | agr_d6 ret es legelo % | agr_d6 ret es legelo % | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_arable_pct | arable pct | agr_d7 szanto% | agr_d7 szanto% | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_pasture_pct | pasture pct | agr_d8 csak legelo% | agr_d8 csak legelo% | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_meadow | meadow | agr_d9 csak rét | agr_d9 csak rét | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_total_area | osszes area | agr_d10 összes terület | agr_d10 összes terület | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_present_population | present population | agr_jelen_nep | agr_jelen_nep | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_agrarian_earners | agrarian kereso 6 | agr_8mezg_kereső (6) | agr_8mezg_kereső (6) | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_total_agrarian_agrarian | total agrarian agrarian | agr_10osz_mezg | agr_10osz_mezg | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_income_pop | jov pop | agr_jöv_nepes10 | agr_jöv_nepes10 | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_income_agrarian_earners | jov agrarian earners | agr_jöv_agrker10 | agr_jöv_agrker10 | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_income_agr_total | jov agr ossz | agr_jöv_agr_össz | agr_jöv_agr_össz | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_holdings_agr_earners | holdings agr earners | agr_birtokiv_agr_keres10 | agr_birtokiv_agr_keres10 | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Agriculture | agr_productive_area_per_agrarian_capita | productive area per agrarian capita | agr_termoter_mezglakos | agr_termoter_mezglakos | GISta Hungarorum JARAS 1910 (ETN/Agrar) |
| hu | Derived share/rate | slovak_share / magyar_share / german_share / ruthen_share | Mother-tongue shares (of pop_present) | (derived) | ||
| hu | Derived share/rate | rel_*_share | Religion shares (of pop_present) | (derived) |
Data sourcesWhat's in the data
Provenance and licences. · source: reference/DATA_SOURCES.md
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/.dbfpolygons + 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 inreference/vs. Rumpler-Seger census XLSX indata/raw/census/); for Upper Hungary both come from the same provider (GISta Hungarorum), split intoshp/andtables/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 aSOURCE.txtgiving 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 insettlements_1910_geothen says where it came from twice over:coord_sourcenames the source (wikidata·ruian_part·ruian_obec·ruian_zsj·geonames·wikidata_vanished·prng) andcoord_refcites 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 incensus.build_census_panels; seePLACE_MATCHING.mdand 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:
- 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árireduces toVár, no chartered town), so all three are curated assk_altindistrict_sk_curation.csv. DECISIONS D-21. - 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 pluralmestá 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. - Right-aligned numeric columns. The
a b 1 2 … 9header 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 inutils/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_codein #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.csvinherits that licence and may only be redistributed under it, with attribution. Recorded inreference/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íkonly 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 forsedlák— the Czech word that means Bauer — covers 6 records againstrolník's 21,461.Citation and licence. MOSAIC states the required form verbatim, and it is what
references.bibemits: 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.savstays in gitignoreddata/and only derived counts reachreference/.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
.savcontains. 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_courts0.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 invanished_placesand incoord_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
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.
Data & reference sources
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.)
| Module | Symbol | Kind |
|---|---|---|
| utils/lexikon_1910/bakeoff.py | select_models | function |
| utils/lexikon_1910/bakeoff.py | runnable | function |
| utils/lexikon_1910/bakeoff.py | run_matrix | function |
| utils/lexikon_1910/bakeoff.py | measure_encoders | function |
| utils/lexikon_1910/bakeoff.py | probe | function |
| utils/lexikon_1910/bakeoff.py | rate_limit_events | function |
| utils/lexikon_1910/bakeoff.py | lexikon_1910_rate_limit_report | function |
| utils/lexikon_1910/build_lexikon_figure.py | Zone | class |
| utils/lexikon_1910/draw_gold_sample.py | has_apparatus | function |
| utils/lexikon_1910/lexikon_1910.py | crop_header | function |
| utils/lexikon_1910/lexikon_1910_census.py | Reconciliation | class |
| utils/lexikon_1910/lexikon_1910_de.py | DeGrid | class |
| utils/lexikon_1910/lexikon_1910_de.py | match_blocks | function |
| utils/lexikon_1910/lexikon_1910_image.py | Heading | class |
| utils/lexikon_1910/lexikon_1910_image.py | lexikon_1910_image_report | function |
| utils/lexikon_1910/lexikon_1910_institutions.py | Component | class |
| utils/lexikon_1910/lexikon_1910_institutions.py | compare_inventories | function |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_inventory_agreement_report | function |
| utils/lexikon_1910/lexikon_1910_institutions.py | symbol_confusion | function |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_symbol_error_report | function |
| utils/lexikon_1910/lexikon_1910_institutions.py | compare_passes | function |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_agreement_report | function |
| utils/lexikon_1910/lexikon_1910_notes.py | ImageCut | class |
| utils/lexikon_1910/lexikon_1910_notes.py | LegendEntry | class |
| utils/lexikon_1910/lexikon_1910_notes.py | referenced_keys | function |
| utils/lexikon_1910/lexikon_1910_notes.py | lexikon_1910_parts_report | function |
| utils/lexikon_1910/lexikon_1910_pages.py | sweep_variant | function |
| utils/lexikon_1910/lexikon_1910_pages.py | RunConfig | class |
| utils/lexikon_1910/lexikon_1910_pages.py | config_report | function |
| utils/lexikon_1910/lexikon_1910_pages.py | estimate_report | function |
| utils/lexikon_1910/lexikon_1910_pages.py | PageRequest | class |
| utils/lexikon_1910/lexikon_1910_pages.py | PageReader | class |
| utils/lexikon_1910/lexikon_1910_pages.py | GooglePageReader | class |
| utils/lexikon_1910/lexikon_1910_pages.py | AnthropicPageReader | class |
| utils/lexikon_1910/lexikon_1910_pages.py | de_pages_for_okres | function |
| utils/lexikon_1910/lexikon_1910_pages.py | okres_name_de | function |
| utils/lexikon_1910/lexikon_1910_pages.py | build_requests | function |
| utils/lexikon_1910/lexikon_1910_pages.py | run_sync | function |
| utils/lexikon_1910/lexikon_1910_pages.py | run_batch | function |
| utils/lexikon_1910/lexikon_1910_pages.py | adopt_batch | function |
| utils/lexikon_1910/lexikon_1910_pages.py | blocks_to_reread | function |
| utils/lexikon_1910/lexikon_1910_pages.py | monitor_report | function |
| utils/lexikon_1910/lexikon_1910_pages.py | assemble_claims | function |
| utils/lexikon_1910/lexikon_1910_pages.py | write_claims | function |
| utils/lexikon_1910/lexikon_1910_pages.py | votes | function |
| utils/lexikon_1910/lexikon_1910_pages.py | vote_report | function |
| utils/lexikon_1910/lexikon_1910_pages.py | lexikon_1910_pages_report | function |
| utils/lexikon_1910/lexikon_1910_pages.py | preflight | function |
| utils/lexikon_1910/lexikon_1910_volume.py | Identity | class |
| utils/lexikon_1910/lexikon_1910_volume.py | HeaderGroup | class |
| utils/lexikon_1910/lexikon_1910_volume.py | VolumeProfile | class |
| utils/lexikon_1910/lexikon_1910_volume.py | lexikon_1910_volume_report | function |
| utils/lexikon_1910/locate_glyphs.py | Lines | class |
| utils/lexikon_1910/locate_glyphs.py | GlyphModel | class |
| utils/lexikon_1910/locate_glyphs.py | Modifiers | class |
| utils/lexikon_1910/run_frozen.py | read_live | function |
| utils/lexikon_1910/run_frozen.py | batch_finish | function |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | OllamaReader | class |
| utils/lexikon_1910/run_r5_german_notes.py | PageCut | class |
| utils/parsing/amateur_theatre.py | okres_tokens | function |
| utils/parsing/amateur_theatre.py | active_decade_list | function |
| utils/parsing/clean.py | strip_place_markers | function |
| utils/parsing/districts.py | attach_gazetteer_names | function |
| utils/parsing/districts.py | write_curation_template | function |
| utils/parsing/districts_hu.py | write_sk_curation_template | function |
| utils/parsing/education.py | clean_education | function |
| utils/parsing/education.py | ladder_report | function |
| utils/parsing/exit_codes.py | label_exit_codes | function |
| utils/parsing/exit_codes.py | parse_exit_date | function |
| utils/parsing/habsburg.py | parse_cause | function |
| utils/parsing/habsburg.py | extract_theatre | function |
| utils/parsing/habsburg.py | parse_ah_units | function |
| utils/parsing/lexikon.py | settlement_slug | function |
| utils/parsing/locations.py | parse_location | function |
| utils/parsing/locative.py | noun_locatives | function |
| utils/parsing/locative.py | adjective_locatives | function |
| utils/parsing/locative.py | noun_genitives | function |
| utils/parsing/locative.py | adjective_genitives | function |
| utils/parsing/ranks.py | english_label | function |
| utils/parsing/ranks.py | resolve_ordinals | function |
| utils/parsing/ranks.py | branch_of | function |
| utils/parsing/ranks.py | common_grade | function |
| utils/parsing/religion.py | translate_religion | function |
| utils/parsing/units.py | extract_regiment | function |
| utils/parsing/units.py | extract_army | function |
| utils/parsing/units.py | translate_unit | function |
| utils/parsing/wikidata.py | add_birth_coords | function |
| utils/plotting/th_style_v113.py | annotate_bars | function |
| utils/plotting/th_style_v113.py | despine | function |
| utils/plotting/th_style_v113.py | add_vspans | function |
| utils/reference_tools/occupations/build_czisco_bridge.py | head_nouns | function |
| utils/reference_tools/places/audit_full_run_pages.py | audit_page | function |
| utils/reference_tools/places/map_intensities.py | Basemap | class |
| utils/reference_tools/units/build_unit_authority.py | headword_number | function |
| utils/scraping/progress.py | has_tqdm | function |
| utils/scraping/scraper_csol.py | ScrapeSummary | class |
| utils/tables/latex_tables.py | regression_table | function |
Full symbol index
1,832 rows
| Module | Symbol | Kind | Called by |
|---|---|---|---|
| utils/docs/build_docs.py | ix_head | function | (internal use only) |
| utils/docs/build_docs.py | favicon_href | function | (internal use only) |
| utils/docs/build_docs.py | md_to_html | function | (internal use only) |
| utils/docs/build_docs.py | build_stamp | function | (internal use only) |
| utils/docs/build_docs.py | collect_scripts | function | (internal use only) |
| utils/docs/build_docs.py | graph_sheet | function | (internal use only) |
| utils/docs/build_docs.py | usage_sheet | function | (internal use only) |
| utils/docs/build_docs.py | changelog_sheet | function | (internal use only) |
| utils/docs/build_docs.py | module_index | function | (internal use only) |
| utils/docs/build_docs.py | df_table | function | (internal use only) |
| utils/docs/build_docs.py | certification_power | function | (internal use only) |
| utils/docs/build_docs.py | rank_reset_hist | function | (internal use only) |
| utils/docs/build_docs.py | rank_reset_areas | function | (internal use only) |
| utils/docs/build_docs.py | render_bib | function | (internal use only) |
| utils/docs/build_docs.py | references_sheet | function | (internal use only) |
| utils/docs/build_docs.py | waterfall_block | function | (internal use only) |
| utils/docs/build_docs.py | waterfall_block_habsburg | function | (internal use only) |
| utils/docs/build_docs.py | provenance_sheet | function | (internal use only) |
| utils/docs/build_docs.py | place_flow | function | (internal use only) |
| utils/docs/build_docs.py | education_figures | function | (internal use only) |
| utils/docs/build_docs.py | theatre_figures | function | (internal use only) |
| utils/docs/build_docs.py | matice_figures | function | (internal use only) |
| utils/docs/build_docs.py | matice_check | function | (internal use only) |
| utils/docs/build_docs.py | intensity_figures | function | (internal use only) |
| utils/docs/build_docs.py | lexikon_page | function | (internal use only) |
| utils/docs/build_docs.py | esc_v | function | (internal use only) |
| utils/docs/build_docs.py | route_flow | function | (internal use only) |
| utils/docs/build_docs.py | occupation_flow | function | (internal use only) |
| utils/docs/build_docs.py | reading_map | function | (internal use only) |
| utils/docs/build_docs.py | ranks_bridge | function | (internal use only) |
| utils/docs/build_docs.py | units_sheet | function | (internal use only) |
| utils/docs/build_docs.py | overview_facts | function | (internal use only) |
| utils/docs/build_docs.py | overview_sheet | function | (internal use only) |
| utils/docs/build_docs.py | live_sheets | function | (internal use only) |
| utils/docs/build_docs.py | working_tree | function | (internal use only) |
| utils/docs/build_docs.py | main | function | (internal use only) |
| utils/docs/build_fonts.py | main | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | find_root | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | host_slug | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | ensure_dirs | function | utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/bakeoff.py | gold_pages | function | 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_frozen.py, utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_table_arm.py |
| utils/lexikon_1910/bakeoff.py | accuracy_pages | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | ollama_show | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | unload | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | daemon_up | function | utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/bakeoff.py | pulled_models | function | utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/bakeoff.py | select_models | function | — no call site found — |
| utils/lexikon_1910/bakeoff.py | runnable | function | — no call site found — |
| utils/lexikon_1910/bakeoff.py | load_encoders | function | utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/bakeoff.py | label_for | function | 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/bakeoff.py | resolution_for | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | flags_for | function | 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/bakeoff.py | applies | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | n_pages | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | plan | function | tests/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.py | load_timings | function | utils/lexikon_1910/run_e1_gemini_version.py, utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/bakeoff.py | estimate | function | tests/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.py | show_plan | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | file_stamp | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | Tee | class | (internal use only) |
| utils/lexikon_1910/bakeoff.py | run_pilot | function | 01_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.py | run_matrix | function | — no call site found — |
| utils/lexikon_1910/bakeoff.py | find_pass_file | function | utils/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.py | load_pass | function | utils/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.py | passes_on_disk | function | utils/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.py | encoder_crop | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | prompt_tokens | function | utils/lexikon_1910/lexikon_1910_pages.py |
| utils/lexikon_1910/bakeoff.py | measure_encoders | function | — no call site found — |
| utils/lexikon_1910/bakeoff.py | probe | function | — no call site found — |
| utils/lexikon_1910/bakeoff.py | rate_limit_events | function | — no call site found — |
| utils/lexikon_1910/bakeoff.py | volume_crops | function | (internal use only) |
| utils/lexikon_1910/bakeoff.py | lexikon_1910_rate_limit_report | function | — no call site found — |
| utils/lexikon_1910/bakeoff.py | de_atlas | function | tests/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.py | de_gold_pages | function | tests/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.py | de_label | function | tests/test_r5_german_notes.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/bakeoff.py | de_passes_on_disk | function | utils/lexikon_1910/score_r5_german_notes.py |
| utils/lexikon_1910/bakeoff.py | summary | function | utils/docs/build_docs.py |
| utils/lexikon_1910/build_contrast_sheet.py | roles_of | function | (internal use only) |
| utils/lexikon_1910/build_contrast_sheet.py | swaps | function | tests/test_contrast_arm.py |
| utils/lexikon_1910/build_contrast_sheet.py | pick_pairs | function | tests/test_contrast_arm.py |
| utils/lexikon_1910/build_contrast_sheet.py | confirmed_boxes | function | (internal use only) |
| utils/lexikon_1910/build_contrast_sheet.py | candidate_cuts | function | (internal use only) |
| utils/lexikon_1910/build_contrast_sheet.py | choose | function | tests/test_contrast_arm.py |
| utils/lexikon_1910/build_contrast_sheet.py | ink_share | function | utils/lexikon_1910/build_symbol_atlas.py |
| utils/lexikon_1910/build_contrast_sheet.py | choose_weighted | function | tests/test_contrast_arm.py |
| utils/lexikon_1910/build_contrast_sheet.py | render | function | utils/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.py | build | function | (internal use only) |
| utils/lexikon_1910/build_contrast_sheet.py | main | function | (internal use only) |
| utils/lexikon_1910/build_de_page_map.py | normalise | function | tests/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.py | parse_head | function | (internal use only) |
| utils/lexikon_1910/build_de_page_map.py | court_crosswalk | function | (internal use only) |
| utils/lexikon_1910/build_de_page_map.py | de_page_words | function | (internal use only) |
| utils/lexikon_1910/build_de_page_map.py | de_page_courts | function | (internal use only) |
| utils/lexikon_1910/build_de_page_map.py | load_de_volume | function | (internal use only) |
| utils/lexikon_1910/build_de_page_map.py | cz_page_names | function | (internal use only) |
| utils/lexikon_1910/build_de_page_map.py | match_page | function | (internal use only) |
| utils/lexikon_1910/build_de_page_map.py | build | function | (internal use only) |
| utils/lexikon_1910/build_de_page_map.py | report | function | 02_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.py | main | function | (internal use only) |
| utils/lexikon_1910/build_glyph_bank.py | line_height | function | utils/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.py | scan_line_height | function | utils/lexikon_1910/locate_glyphs.py |
| utils/lexikon_1910/build_glyph_bank.py | to_source | function | (internal use only) |
| utils/lexikon_1910/build_glyph_bank.py | tight_box | function | utils/reference_tools/places/run_german_oracle.py, utils/reference_tools/places/run_german_reader_boxes.py |
| utils/lexikon_1910/build_glyph_bank.py | cut | function | tests/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.py | contact_sheets | function | (internal use only) |
| utils/lexikon_1910/build_glyph_bank.py | main | function | (internal use only) |
| utils/lexikon_1910/build_lexikon_figure.py | Zone | class | — no call site found — |
| utils/lexikon_1910/build_lexikon_figure.py | Boxer | class | (internal use only) |
| utils/lexikon_1910/build_lexikon_figure.py | czech_page | function | (internal use only) |
| utils/lexikon_1910/build_lexikon_figure.py | german_page | function | (internal use only) |
| utils/lexikon_1910/build_lexikon_figure.py | pilot_counts | function | (internal use only) |
| utils/lexikon_1910/build_lexikon_figure.py | glyphs | function | utils/reference_tools/places/run_german_oracle.py |
| utils/lexikon_1910/build_lexikon_figure.py | build | function | (internal use only) |
| utils/lexikon_1910/build_lexikon_figure.py | main | function | (internal use only) |
| utils/lexikon_1910/build_mark_ledger.py | paths | function | utils/lexikon_1910/lexikon_1910_pages.py |
| utils/lexikon_1910/build_mark_ledger.py | configure | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/build_mark_ledger.py | section_of | function | (internal use only) |
| utils/lexikon_1910/build_mark_ledger.py | reader_events | function | tests/test_route_fixes.py |
| utils/lexikon_1910/build_mark_ledger.py | order_events | function | (internal use only) |
| utils/lexikon_1910/build_mark_ledger.py | opus_events | function | tests/test_abbreviations.py, tests/test_opus_cards.py, tests/test_route_fixes.py |
| utils/lexikon_1910/build_mark_ledger.py | author_events | function | (internal use only) |
| utils/lexikon_1910/build_mark_ledger.py | unplaced_gold | function | (internal use only) |
| utils/lexikon_1910/build_mark_ledger.py | build | function | (internal use only) |
| utils/lexikon_1910/build_mark_ledger.py | report | function | 02_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.py | trails | function | (internal use only) |
| utils/lexikon_1910/build_mark_ledger.py | main | function | (internal use only) |
| utils/lexikon_1910/build_register_walkthrough.py | build | function | (internal use only) |
| utils/lexikon_1910/build_register_walkthrough.py | main | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas.py | cluster_lines | function | utils/lexikon_1910/build_symbol_atlas_de.py |
| utils/lexikon_1910/build_symbol_atlas.py | columns_of | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas.py | load_targets | function | utils/lexikon_1910/build_symbol_atlas_de.py |
| utils/lexikon_1910/build_symbol_atlas.py | match_gloss | function | utils/lexikon_1910/build_symbol_atlas_de.py |
| utils/lexikon_1910/build_symbol_atlas.py | build | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas.py | ink_share | function | utils/lexikon_1910/build_contrast_sheet.py |
| utils/lexikon_1910/build_symbol_atlas.py | main | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas_de.py | load_targets | function | utils/lexikon_1910/build_symbol_atlas.py |
| utils/lexikon_1910/build_symbol_atlas_de.py | plate_words | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas_de.py | dark_mask | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas_de.py | plate_ink | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas_de.py | drop_rules | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas_de.py | ink_columns | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas_de.py | plate_entries | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas_de.py | body_rows | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas_de.py | build | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas_de.py | borrow | function | (internal use only) |
| utils/lexikon_1910/build_symbol_atlas_de.py | compose | function | tests/test_glyph_e3.py, utils/lexikon_1910/locate_glyphs.py, utils/lexikon_1910/run_glyph_e3.py |
| utils/lexikon_1910/build_symbol_atlas_de.py | report | function | 02_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.py | main | function | (internal use only) |
| utils/lexikon_1910/build_token_review.py | suspects_in_sheet | function | (internal use only) |
| utils/lexikon_1910/build_token_review.py | candidates | function | utils/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.py | text_lines | function | utils/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.py | municipality_bands | function | utils/lexikon_1910/locate_glyphs.py, utils/reference_tools/places/build_german_click_queue.py |
| utils/lexikon_1910/build_token_review.py | challenges | function | (internal use only) |
| utils/lexikon_1910/build_token_review.py | headings | function | utils/docs/build_docs.py |
| utils/lexikon_1910/build_token_review.py | box_items | function | (internal use only) |
| utils/lexikon_1910/build_token_review.py | build | function | (internal use only) |
| utils/lexikon_1910/build_token_review.py | write_heading_rulings | function | (internal use only) |
| utils/lexikon_1910/build_token_review.py | apply | function | 02_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.py | main | function | (internal use only) |
| utils/lexikon_1910/check_transcription.py | split_entries | function | utils/parsing/vua_courts.py |
| utils/lexikon_1910/check_transcription.py | parse | function | (internal use only) |
| utils/lexikon_1910/check_transcription.py | main | function | (internal use only) |
| utils/lexikon_1910/draw_gold_sample.py | draw | function | utils/docs/build_docs.py, utils/lexikon_1910/locate_glyphs.py, utils/reference_tools/places/map_intensities.py |
| utils/lexikon_1910/draw_gold_sample.py | has_apparatus | function | — no call site found — |
| utils/lexikon_1910/draw_gold_sample.py | draw_volume | function | tests/test_ms_sample.py |
| utils/lexikon_1910/draw_gold_sample.py | main | function | (internal use only) |
| utils/lexikon_1910/extract_prose_pages.py | table_foot | function | utils/lexikon_1910/lexikon_1910_notes.py |
| utils/lexikon_1910/extract_prose_pages.py | lines | function | utils/reference_tools/places/statutory_prose.py |
| utils/lexikon_1910/extract_prose_pages.py | paragraphs | function | (internal use only) |
| utils/lexikon_1910/extract_prose_pages.py | vision_read | function | (internal use only) |
| utils/lexikon_1910/extract_prose_pages.py | main | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910.py | summary_label | function | tests/test_de_table.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/lexikon_1910_de_table.py |
| utils/lexikon_1910/lexikon_1910.py | Invariant | class | utils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/read_transcription_md.py |
| utils/lexikon_1910/lexikon_1910.py | parse_int_cell | function | utils/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.py | AltoPage | class | tests/test_lexikon_1910_table_foot.py |
| utils/lexikon_1910/lexikon_1910.py | read_alto | function | utils/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.py | column_boundaries | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910.py | column_template | function | utils/lexikon_1910/make_gold_worksheet.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910.py | fit_columns | function | utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910.py | write_grid_template | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910.py | read_grid_template | function | 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/lexikon_1910.py | body_band | function | tests/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.py | table_bottom | function | tests/test_lexikon_1910_table_foot.py |
| utils/lexikon_1910/lexikon_1910.py | header_foot | function | tests/test_lexikon_1910_table_top.py |
| utils/lexikon_1910/lexikon_1910.py | table_top | function | tests/test_lexikon_1910_table_top.py |
| utils/lexikon_1910/lexikon_1910.py | Row | class | tests/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.py | extract_rows | function | tests/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.py | Block | class | tests/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.py | segment_blocks | function | utils/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.py | carry_block | function | tests/test_lexikon_1910_pilot_checks.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910.py | summary_rows | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910.py | grade_row | function | 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/lexikon_1910.py | grade_block_sum | function | tests/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.py | grade_sequence | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910.py | grade_page | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910.py | render_page | function | utils/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.py | running_head | function | 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_frozen.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910.py | crop_block | function | utils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/run_de_table.py |
| utils/lexikon_1910/lexikon_1910.py | scale_to_width | function | utils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/lexikon_1910_notes.py |
| utils/lexikon_1910/lexikon_1910.py | tile_block | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910.py | crop_header | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910.py | lexikon_1910_report | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_attributes.py | roman | function | tests/test_lexikon_1910_attributes.py |
| utils/lexikon_1910/lexikon_1910_attributes.py | schools | function | tests/test_lexikon_1910_attributes.py |
| utils/lexikon_1910/lexikon_1910_attributes.py | cooperatives | function | tests/test_lexikon_1910_attributes.py |
| utils/lexikon_1910/lexikon_1910_attributes.py | attribute_records | function | tests/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.py | entry_parts | function | utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_attributes.py | score_attributes | function | tests/test_lexikon_1910_attributes.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_attributes.py | lexikon_1910_attributes_report | function | tests/test_lexikon_1910_attributes.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_census.py | is_summary_row | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_census.py | Reconciliation | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_census.py | register_totals | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_census.py | census_targets | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_census.py | house_candidates | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_census.py | reconcile | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_census.py | diagnose | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_census.py | lexikon_1910_census_report | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_courts.py | slug | function | tests/test_court_key.py |
| utils/lexikon_1910/lexikon_1910_courts.py | slug_variants | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_courts.py | load_backbone | function | tests/test_court_key.py, tests/test_prefreeze_route.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/lexikon_1910_courts.py | court_forms | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_courts.py | court_id | function | tests/test_route_fixes.py |
| utils/lexikon_1910/lexikon_1910_courts.py | near | function | tests/test_court_key.py, tests/test_lexikon_1910_image.py, utils/parsing/ruian.py |
| utils/lexikon_1910/lexikon_1910_courts.py | head_courts | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_courts.py | load_aliases | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_courts.py | CourtKey | class | tests/test_court_key.py, utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/score_certification.py |
| utils/lexikon_1910/lexikon_1910_courts.py | lexikon_1910_courts_report | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/lexikon_1910_crosswalk.py | german_key | function | tests/test_register_build.py |
| utils/lexikon_1910/lexikon_1910_crosswalk.py | pair_court | function | tests/test_register_build.py, utils/lexikon_1910/build_register_walkthrough.py |
| utils/lexikon_1910/lexikon_1910_crosswalk.py | pair_parts | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_crosswalk.py | order_holdout | function | tests/test_register_build.py, utils/lexikon_1910/build_register_walkthrough.py |
| utils/lexikon_1910/lexikon_1910_crosswalk.py | crosswalk_editions | function | tests/test_register_build.py, utils/lexikon_1910/lexikon_1910_register.py |
| utils/lexikon_1910/lexikon_1910_crosswalk.py | crosswalk_report | function | utils/lexikon_1910/lexikon_1910_register.py, utils/parsing/ruian.py |
| utils/lexikon_1910/lexikon_1910_de.py | normalise_name | function | tests/test_amateur_theatre.py, utils/lexikon_1910/lexikon_1910_pages.py, utils/parsing/amateur_theatre.py |
| utils/lexikon_1910/lexikon_1910_de.py | build_page_index | function | utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_de.py | okres_pages | function | utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_de.py | DeGrid | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_de.py | fit_grid | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_de.py | extract_rows_de | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_de.py | is_heading | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_de.py | match_blocks | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_de.py | compare_cells | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_de.py | lexikon_1910_de_report | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_de_notes.py | header_system_de | function | tests/test_de_table.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_de_notes.py | build_system_de | function | tests/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.py | native_width | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_de_notes.py | render_native | function | tests/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.py | strip_height | function | tests/test_r5_german_notes.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_de_notes.py | empty_notes | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_de_notes.py | cut_page | function | utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_de_notes.py | last_heading_crop | function | utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_de_notes.py | header_crop | function | utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_de_notes.py | header_check | function | tests/test_r5_german_notes.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_de_notes.py | fold | function | tests/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.py | expand_court_abbreviations | function | tests/test_route_fixes.py, utils/lexikon_1910/lexikon_1910_courts.py |
| utils/lexikon_1910/lexikon_1910_de_notes.py | name_forms | function | tests/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.py | load_court_crosswalk | function | tests/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.py | court_name | function | tests/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.py | relabel_sections | function | tests/test_r5_german_notes.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_de_table.py | PrintedRow | class | tests/test_cache_guard.py, tests/test_de_table.py |
| utils/lexikon_1910/lexikon_1910_de_table.py | TableBlock | class | tests/test_de_table.py |
| utils/lexikon_1910/lexikon_1910_de_table.py | column_index | function | tests/test_lexikon_1910_volume.py, utils/lexikon_1910/lexikon_1910_volume.py |
| utils/lexikon_1910/lexikon_1910_de_table.py | detect | function | tests/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.py | table_ink | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_de_table.py | printed_rows | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_de_table.py | cut_blocks | function | tests/test_cache_guard.py, tests/test_de_table.py |
| utils/lexikon_1910/lexikon_1910_de_table.py | page_blocks | function | tests/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.py | crop_block | function | utils/lexikon_1910/lexikon_1910.py, utils/lexikon_1910/run_de_table.py |
| utils/lexikon_1910/lexikon_1910_de_table.py | build_system | function | tests/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.py | row_schema | function | tests/test_de_table.py, utils/lexikon_1910/run_de_table.py |
| utils/lexikon_1910/lexikon_1910_de_table.py | grade_block | function | tests/test_de_table.py, utils/lexikon_1910/run_de_table.py |
| utils/lexikon_1910/lexikon_1910_de_table.py | summary_label | function | tests/test_de_table.py, utils/lexikon_1910/build_lexikon_figure.py, utils/lexikon_1910/lexikon_1910.py |
| utils/lexikon_1910/lexikon_1910_de_table.py | court_labels | function | tests/test_de_table.py, utils/lexikon_1910/run_de_table.py |
| utils/lexikon_1910/lexikon_1910_de_table.py | table_rows | function | tests/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.py | lexikon_1910_de_table_report | function | utils/lexikon_1910/run_de_table.py |
| utils/lexikon_1910/lexikon_1910_editions.py | name_forms | function | tests/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.py | pair_units | function | tests/test_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_editions.py | pair_by_forms | function | tests/test_cz_anchor.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/lexikon_1910_editions.py | unit_claims | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_editions.py | truth_items | function | tests/test_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_editions.py | joint_errors | function | tests/test_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_editions.py | compare_pair | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_editions.py | keep_codes | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_editions.py | compare_editions | function | 15_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.py | lexikon_1910_editions_report | function | 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_image.py | Rule | class | tests/test_lexikon_1910_image.py |
| utils/lexikon_1910/lexikon_1910_image.py | Line | class | tests/test_cz_anchor.py |
| utils/lexikon_1910/lexikon_1910_image.py | Heading | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_image.py | Segment | class | tests/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.py | PageGeometry | class | tests/test_lexikon_1910_image.py |
| utils/lexikon_1910/lexikon_1910_image.py | to_grey | function | utils/lexikon_1910/lexikon_1910_de_table.py, utils/lexikon_1910/lexikon_1910_notes.py |
| utils/lexikon_1910/lexikon_1910_image.py | ink_threshold | function | utils/lexikon_1910/lexikon_1910_de_table.py |
| utils/lexikon_1910/lexikon_1910_image.py | sheet_box | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_image.py | vertical_rules | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_image.py | horizontal_rules | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_image.py | rule_mask | function | tests/test_lexikon_1910_image.py, utils/lexikon_1910/lexikon_1910_notes.py |
| utils/lexikon_1910/lexikon_1910_image.py | ink_bands | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_image.py | lines_in | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_image.py | find_headings | function | tests/test_cz_anchor.py |
| utils/lexikon_1910/lexikon_1910_image.py | blank_cuts | function | tests/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.py | detect | function | tests/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.py | is_edge | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_image.py | segments | function | tests/test_lexikon_1910_image.py |
| utils/lexikon_1910/lexikon_1910_image.py | crops | function | 15_frozen_run.ipynb, tests/test_lexikon_1910_image.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_image.py | uncovered | function | tests/test_lexikon_1910_image.py |
| utils/lexikon_1910/lexikon_1910_image.py | zone_of | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_image.py | lexikon_1910_image_report | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_institutions.py | Abbreviation | class | tests/test_lexikon_1910_order.py, tests/test_pooled_score.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | load_abbreviations | function | tests/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.py | load_religion_categories | function | tests/test_religion_categories.py, utils/lexikon_1910/lexikon_1910_register.py, utils/lexikon_1910/score_certification.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | Entry | class | tests/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.py | parse_entries | function | tests/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.py | cooperative_colon_rule | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_institutions.py | bracketed_glyph_rule | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_institutions.py | decode_entry | function | tests/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.py | decode_sections | function | tests/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.py | Component | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_institutions.py | load_components | function | tests/test_abbreviations.py, utils/lexikon_1910/report_unlisted_abbreviations.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | expand_abbreviated | function | tests/test_abbreviations.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | split_items | function | tests/test_abbreviations.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | unlisted_items | function | tests/test_abbreviations.py, utils/lexikon_1910/report_unlisted_abbreviations.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | unlisted_report | function | 15_frozen_run.ipynb, tests/test_abbreviations.py, utils/lexikon_1910/report_unlisted_abbreviations.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | join_symbols | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_join_report | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | place_index | function | utils/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.py | attach_places | function | utils/lexikon_1910/lexikon_1910_pages.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_places_joined_report | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | build_inventory | function | tests/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.py | lexikon_1910_inventory_report | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | compare_inventories | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_inventory_agreement_report | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_institutions.py | describe_reference | function | utils/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.py | score_against_reference | function | tests/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.py | lexikon_1910_accuracy_report | function | tests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | symbol_confusion | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_symbol_error_report | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_institutions.py | symbol_rates | function | tests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_symbol_rates_report | function | tests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | part_attribution | function | tests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_part_attribution_report | function | tests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | clustered_interval | function | tests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_clustered_interval_report | function | tests/test_lexikon_1910_scoring.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | second_reader | function | tests/test_lexikon_1910_second_reader.py, utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_second_reader_report | function | tests/test_lexikon_1910_second_reader.py, utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | order_runs | function | tests/test_lexikon_1910_order.py, utils/lexikon_1910/read_transcription_md.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | learn_order | function | tests/test_lexikon_1910_order.py, tests/test_repeated_marks.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | out_of_order | function | tests/test_lexikon_1910_order.py, tests/test_unit_timeline.py, utils/reference_tools/units/build_unit_timeline.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | order_restart | function | tests/test_lexikon_1910_order.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | repeatable_marks | function | tests/test_repeated_marks.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | repeated_marks | function | tests/test_repeated_marks.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | order_allows | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_institutions.py | order_supports | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_institutions.py | repeat_candidates | function | tests/test_repeated_marks.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | check_order | function | tests/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.py | position_swaps | function | tests/test_lookalike_rules.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | order_summary | function | tests/test_lexikon_1910_order.py, tests/test_repeated_marks.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | order_reference_flags | function | tests/test_lexikon_1910_order.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | order_joins | function | tests/test_lexikon_1910_order.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | order_sheet_rows | function | utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | order_scores | function | tests/test_lexikon_1910_order.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | order_disputes | function | tests/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.py | lexikon_1910_order_report | function | tests/test_lexikon_1910_order.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_order_disputes_report | function | tests/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.py | compare_passes | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_institutions.py | disagreements | function | utils/lexikon_1910/lexikon_1910_editions.py, utils/reference_tools/occupations/run_occcanine_check.py |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_agreement_report | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_institutions.py | lexikon_1910_institutions_report | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_ledger.py | Event | class | tests/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.py | box_mark | function | tests/test_cz_anchor.py, tests/test_route_fixes.py, utils/lexikon_1910/build_mark_ledger.py |
| utils/lexikon_1910/lexikon_1910_ledger.py | unplaced_mark | function | tests/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.py | write_ledger | function | tests/test_lexikon_1910_ledger.py, utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/lexikon_1910_ledger.py | read_ledger | function | tests/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.py | by_mark | function | utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/lexikon_1910_ledger.py | majority | function | utils/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.py | flagged | function | tests/test_verify_test_b.py, utils/lexikon_1910/run_verify_test_b.py, utils/parsing/vua_courts.py |
| utils/lexikon_1910/lexikon_1910_ledger.py | final_value | function | tests/test_lexikon_1910_ledger.py |
| utils/lexikon_1910/lexikon_1910_ledger.py | with_final | function | tests/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.py | is_claim_mark | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_ledger.py | claim_rulings | function | tests/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.py | running_values | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_ledger.py | step_changes | function | tests/test_lexikon_1910_ledger.py, utils/lexikon_1910/build_mark_ledger.py |
| utils/lexikon_1910/lexikon_1910_ledger.py | trail | function | utils/lexikon_1910/build_mark_ledger.py |
| utils/lexikon_1910/lexikon_1910_names.py | fold_lookalikes | function | tests/test_lexikon_1910_running_head.py, utils/lexikon_1910/lexikon_1910_courts.py |
| utils/lexikon_1910/lexikon_1910_names.py | shape_key | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_names.py | head_politicals | function | tests/test_lexikon_1910_running_head.py, utils/lexikon_1910/lexikon_1910_courts.py |
| utils/lexikon_1910/lexikon_1910_names.py | normalise | function | tests/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.py | parse_name_cell | function | utils/lexikon_1910/build_register_walkthrough.py, utils/lexikon_1910/lexikon_1910_crosswalk.py |
| utils/lexikon_1910/lexikon_1910_names.py | Obec | class | utils/lexikon_1910/lexikon_1910_crosswalk.py |
| utils/lexikon_1910/lexikon_1910_names.py | Authority | class | tests/test_court_key.py, tests/test_lexikon_1910_running_head.py |
| utils/lexikon_1910/lexikon_1910_names.py | assign_okresy | function | tests/test_lexikon_1910_running_head.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_names.py | build_authority | function | 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.py |
| utils/lexikon_1910/lexikon_1910_names.py | add_register_okresy | function | tests/test_lexikon_1910_running_head.py |
| utils/lexikon_1910/lexikon_1910_names.py | Anchor | class | (internal use only) |
| utils/lexikon_1910/lexikon_1910_names.py | anchor_block | function | utils/lexikon_1910/lexikon_1910_register.py |
| utils/lexikon_1910/lexikon_1910_names.py | anchor_blocks | function | utils/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.py | harvest_printed_names | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_names.py | pair_by_obec | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_names.py | anchor_report | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_notes.py | legend_key_marks | function | utils/lexikon_1910/read_transcription_md.py |
| utils/lexikon_1910/lexikon_1910_notes.py | normalise | function | tests/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.py | find_heading | function | tests/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.py | Section | class | tests/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.py | Notes | class | tests/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.py | divider_rule | function | tests/test_lexikon_1910_apparatus_coverage.py, tests/test_lexikon_1910_table_foot.py |
| utils/lexikon_1910/lexikon_1910_notes.py | fallback_divider | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_notes.py | table_foot | function | utils/lexikon_1910/extract_prose_pages.py |
| utils/lexikon_1910/lexikon_1910_notes.py | apparatus_band | function | utils/lexikon_1910/lexikon_1910.py |
| utils/lexikon_1910/lexikon_1910_notes.py | extract_notes | function | tests/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.py | notes_boxes | function | tests/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.py | line_cuts | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_notes.py | crop_notes | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_notes.py | Segment | class | tests/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.py | trailing_section | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_notes.py | section_segments | function | tests/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.py | crop_segments | function | utils/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.py | ImageCut | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_notes.py | page_ink | function | tests/test_lexikon_1910_pilot_geometry.py |
| utils/lexikon_1910/lexikon_1910_notes.py | text_headings | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/lexikon_1910_notes.py | image_section_segments | function | tests/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.py | trailing_from_segments | function | tests/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.py | collect_sections | function | utils/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.py | LegendEntry | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_notes.py | postal_legend | function | utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_notes.py | parse_reference | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_notes.py | parse_distance | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_notes.py | match_district | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_notes.py | resolve_places | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_notes.py | lexikon_1910_places_report | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_notes.py | keys_by_district | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_notes.py | parts_by_district | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_notes.py | altitudes | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_notes.py | referenced_keys | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_notes.py | court_of | function | utils/lexikon_1910/score_certification.py, utils/reference_tools/places/score_sealed_abbreviations.py |
| utils/lexikon_1910/lexikon_1910_notes.py | entry_chunks | function | utils/lexikon_1910/lexikon_1910_institutions.py |
| utils/lexikon_1910/lexikon_1910_notes.py | split_parts | function | tests/test_lexikon_1910_parts_score.py, utils/lexikon_1910/read_transcription_md.py |
| utils/lexikon_1910/lexikon_1910_notes.py | parts_records | function | utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_notes.py | lexikon_1910_parts_report | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_notes.py | score_parts | function | tests/test_lexikon_1910_parts_score.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_notes.py | lexikon_1910_parts_score_report | function | tests/test_lexikon_1910_parts_score.py, utils/lexikon_1910/rescore_passes.py |
| utils/lexikon_1910/lexikon_1910_notes.py | parts_check | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_notes.py | lexikon_1910_notes_report | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_pages.py | ImageRule | class | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | ModelSpec | class | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | load_credentials | function | 15_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.py | save_credentials | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/lexikon_1910_pages.py | mask | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | credentials_report | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | check_credentials | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | sweep_variant | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | prompt_tokens | function | utils/lexikon_1910/bakeoff.py |
| utils/lexikon_1910/lexikon_1910_pages.py | RunConfig | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | config_report | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | Tile | class | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | plan_tiles | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | tile_tokens | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | cut_tiles | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | rule_cuts | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | page_bands | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | plan_bands | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | to_gemini_schema | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | build_page_system | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | fewshot_from_gold | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | load_fewshot | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | Usage | class | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | append_ledger | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | read_ledger | function | tests/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.py | page_size | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | estimate | function | tests/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.py | project | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | estimate_report | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | PageRequest | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | JobHandle | class | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | PageReader | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | encode_png | function | utils/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.py | GooglePageReader | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | AnthropicPageReader | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | build_reader | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_pages.py | custom_id | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | render | function | utils/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.py | detect_divider | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | apparatus_divider | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | load_de_page_map | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | de_pages_for_cz | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | de_pages_for_okres | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | okres_name_de | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | alto_page | function | utils/lexikon_1910/build_de_page_map.py |
| utils/lexikon_1910/lexikon_1910_pages.py | build_requests | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | chunk_by_size | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | pending | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | run_sync | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | run_batch | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | known_job_ids | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | batches_report | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | adopt_batch | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | cancel_batch | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | load_jobs | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | collect_jobs | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | claims_from_payload | function | utils/lexikon_1910/run_e1_gemini_version.py |
| utils/lexikon_1910/lexikon_1910_pages.py | normalise_symbol | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | grade_page_table | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | blocks_to_reread | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | monitor_report | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | place_index | function | utils/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.py | attach_places | function | utils/lexikon_1910/lexikon_1910_institutions.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_pages.py | load_results | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_pages.py | assemble_claims | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | write_claims | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | votes | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | route | function | utils/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.py | vote_report | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | lexikon_1910_pages_report | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | preflight | function | — no call site found — |
| utils/lexikon_1910/lexikon_1910_pages.py | load_abbreviations | function | tests/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.py | RunInput | class | tests/test_de_table.py, tests/test_register_build.py |
| utils/lexikon_1910/lexikon_1910_register.py | parse_run_name | function | tests/test_register_build.py |
| utils/lexikon_1910/lexikon_1910_register.py | name_source_paths | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | load_run | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | record_extra_paths | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | court_key | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/lexikon_1910_register.py | clean_display | function | tests/test_register_build.py |
| utils/lexikon_1910/lexikon_1910_register.py | cell_number | function | tests/test_register_figures.py |
| utils/lexikon_1910/lexikon_1910_register.py | row_figures | function | tests/test_register_figures.py |
| utils/lexikon_1910/lexikon_1910_register.py | walking_hours | function | tests/test_register_figures.py |
| utils/lexikon_1910/lexikon_1910_register.py | keyed_references | function | tests/test_register_figures.py |
| utils/lexikon_1910/lexikon_1910_register.py | legend_items | function | tests/test_register_figures.py |
| utils/lexikon_1910/lexikon_1910_register.py | place_references | function | tests/test_register_figures.py |
| utils/lexikon_1910/lexikon_1910_register.py | register_units | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | load_authority | function | utils/lexikon_1910/build_register_walkthrough.py |
| utils/lexikon_1910/lexikon_1910_register.py | anchor_places | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | settled_by | function | tests/test_register_build.py |
| utils/lexikon_1910/lexikon_1910_register.py | build_institutions | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | build_cooperatives | function | tests/test_register_build.py |
| utils/lexikon_1910/lexikon_1910_register.py | spread_names | function | tests/test_register_build.py |
| utils/lexikon_1910/lexikon_1910_register.py | spread_columns | function | tests/test_register_build.py |
| utils/lexikon_1910/lexikon_1910_register.py | municipality_owner | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | build_municipalities | function | tests/test_register_build.py |
| utils/lexikon_1910/lexikon_1910_register.py | school_type | function | tests/test_register_figures.py |
| utils/lexikon_1910/lexikon_1910_register.py | school_rung | function | tests/test_register_figures.py |
| utils/lexikon_1910/lexikon_1910_register.py | school_columns | function | tests/test_register_figures.py |
| utils/lexikon_1910/lexikon_1910_register.py | build_run | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | build_statutory | function | tests/test_register_figures.py |
| utils/lexikon_1910/lexikon_1910_register.py | build_register | function | 15_frozen_run.ipynb, tests/test_register_build.py, utils/lexikon_1910/build_register_walkthrough.py |
| utils/lexikon_1910/lexikon_1910_register.py | crosswalk_stale | function | tests/test_register_build.py |
| utils/lexikon_1910/lexikon_1910_register.py | anchor_summary | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | register_report | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/lexikon_1910_register.py | okres_check | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | birthplace_join | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | register_checks | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/lexikon_1910_register.py | run_folders | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/lexikon_1910_register.py | measure_anchoring | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/lexikon_1910_register.py | undocumented_columns | function | utils/validation/data_checks.py |
| utils/lexikon_1910/lexikon_1910_register.py | build_block | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_register.py | write_build_block | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/lexikon_1910_vision.py | canonical | function | utils/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.py | pictograms | function | tests/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.py | vocabulary | function | tests/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.py | build_system | function | tests/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.py | lines_from_text | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_vision.py | guard_tokens | function | utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_vision.py | drop_repeats | function | utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_vision.py | symbols_check | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_vision.py | symbols_check_sections | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_vision.py | content_check | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_vision.py | notes_from_regions | function | utils/lexikon_1910/report_unlisted_abbreviations.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_vision.py | sections_from_segments | function | tests/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.py | compare_notes | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_vision.py | compare_sections | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_vision.py | lexikon_1910_vision_report | function | utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/lexikon_1910_volume.py | Column | class | tests/test_r5_german_notes.py |
| utils/lexikon_1910/lexikon_1910_volume.py | Identity | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_volume.py | HeaderGroup | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_volume.py | VolumeProfile | class | — no call site found — |
| utils/lexikon_1910/lexikon_1910_volume.py | normalise | function | tests/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.py | parse_profile | function | (internal use only) |
| utils/lexikon_1910/lexikon_1910_volume.py | load_profile | function | tests/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.py | profiles | function | tests/test_lexikon_1910_volume.py |
| utils/lexikon_1910/lexikon_1910_volume.py | czech_drift | function | tests/test_lexikon_1910_volume.py |
| utils/lexikon_1910/lexikon_1910_volume.py | lexikon_1910_volume_report | function | — no call site found — |
| utils/lexikon_1910/locate_glyphs.py | Parts | class | tests/test_glyph_e3.py, utils/lexikon_1910/run_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | composites | function | tests/test_glyph_e3.py, utils/lexikon_1910/run_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | parts_of | function | tests/test_glyph_e3.py, utils/lexikon_1910/run_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | base_of | function | tests/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.py | compose | function | tests/test_glyph_e3.py, utils/lexikon_1910/build_symbol_atlas_de.py, utils/lexikon_1910/run_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | Claim | class | tests/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.py | Section | class | tests/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.py | gold_sheets | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | claims_in_answer | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | section_district | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | read_sections | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | Candidate | class | tests/test_glyph_e3.py, utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/locate_glyphs.py | ink_mask | function | utils/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.py | bitmap | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | shape_features | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | baselines | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | bracket_kind | function | tests/test_glyph_e3.py, utils/reference_tools/places/german_candidate_diagnosis.py |
| utils/lexikon_1910/locate_glyphs.py | stacked_pieces | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | Lines | class | — no call site found — |
| utils/lexikon_1910/locate_glyphs.py | line_components | function | utils/reference_tools/places/german_candidate_diagnosis.py |
| utils/lexikon_1910/locate_glyphs.py | candidates_on_image | function | tests/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.py | section_candidates | function | utils/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.py | tag_entries | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | heuristic_glyphness | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | feature_matrix | function | utils/reference_tools/places/german_supervised_placement.py |
| utils/lexikon_1910/locate_glyphs.py | GlyphModel | class | — no call site found — |
| utils/lexikon_1910/locate_glyphs.py | fit_glyph_model | function | utils/reference_tools/places/german_supervised_placement.py |
| utils/lexikon_1910/locate_glyphs.py | align | function | tests/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.py | entry_mask | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | place | function | tests/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.py | load_sections | function | utils/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.py | plate_templates | function | utils/reference_tools/places/german_locator_trial.py |
| utils/lexikon_1910/locate_glyphs.py | Locator | class | tests/test_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | learn_templates | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | train_locator | function | utils/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.py | locate_all | function | utils/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.py | Modifiers | class | — no call site found — |
| utils/lexikon_1910/locate_glyphs.py | letter_shaped | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | is_speck | function | utils/reference_tools/places/run_german_detect_by_naming.py, utils/reference_tools/places/run_german_reader_boxes.py |
| utils/lexikon_1910/locate_glyphs.py | line_neighbours | function | utils/lexikon_1910/run_glyph_e3.py, utils/reference_tools/places/run_german_oracle.py |
| utils/lexikon_1910/locate_glyphs.py | modifiers | function | tests/test_glyph_e3.py, utils/lexikon_1910/run_glyph_e3.py, utils/reference_tools/places/run_german_oracle.py |
| utils/lexikon_1910/locate_glyphs.py | modifier_rows | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | modifier_text | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | place_blind | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | line_starts | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | key_left | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | text_column | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | detector_features | function | utils/lexikon_1910/run_e3_pages.py |
| utils/lexikon_1910/locate_glyphs.py | eligible | function | utils/lexikon_1910/run_e3_pages.py, utils/parsing/vua_courts.py |
| utils/lexikon_1910/locate_glyphs.py | fit_detector | function | utils/lexikon_1910/run_e3_pages.py |
| utils/lexikon_1910/locate_glyphs.py | best_threshold | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | section_text_columns | function | utils/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.py | Detection | class | utils/lexikon_1910/run_e3_pages.py |
| utils/lexikon_1910/locate_glyphs.py | detect_marks | function | utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_glyph_e3.py |
| utils/lexikon_1910/locate_glyphs.py | inside | function | 16_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.py | match_clicks | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | false_boxes | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | summarise | function | utils/scraping/run_lexicon_downloads.py, utils/scraping/run_oeulk_downloads.py |
| utils/lexikon_1910/locate_glyphs.py | box_stability | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | stability_text | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | draw | function | utils/docs/build_docs.py, utils/lexikon_1910/draw_gold_sample.py, utils/reference_tools/places/map_intensities.py |
| utils/lexikon_1910/locate_glyphs.py | write_boxes | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | write_candidates | function | (internal use only) |
| utils/lexikon_1910/locate_glyphs.py | main | function | (internal use only) |
| utils/lexikon_1910/make_gold_worksheet.py | build | function | (internal use only) |
| utils/lexikon_1910/make_gold_worksheet.py | main | function | (internal use only) |
| utils/lexikon_1910/make_gold_worksheet_de.py | load_manifest | function | (internal use only) |
| utils/lexikon_1910/make_gold_worksheet_de.py | render | function | utils/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.py | sheet_text | function | (internal use only) |
| utils/lexikon_1910/make_gold_worksheet_de.py | build | function | (internal use only) |
| utils/lexikon_1910/make_gold_worksheet_de.py | main | function | (internal use only) |
| utils/lexikon_1910/read_transcription_md.py | front_matter | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/read_transcription_md.py | read_offices | function | (internal use only) |
| utils/lexikon_1910/read_transcription_md.py | read_office_keys | function | tests/test_sealed_scorer.py |
| utils/lexikon_1910/read_transcription_md.py | read_parts | function | (internal use only) |
| utils/lexikon_1910/read_transcription_md.py | read_table | function | (internal use only) |
| utils/lexikon_1910/read_transcription_md.py | grade_table | function | tests/test_lexikon_1910_block_carry.py |
| utils/lexikon_1910/read_transcription_md.py | grade_parts | function | (internal use only) |
| utils/lexikon_1910/read_transcription_md.py | read_legend | function | (internal use only) |
| utils/lexikon_1910/read_transcription_md.py | read_notes | function | (internal use only) |
| utils/lexikon_1910/read_transcription_md.py | read_attributes | function | (internal use only) |
| utils/lexikon_1910/read_transcription_md.py | read_worked_examples | function | tests/test_repeated_marks.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/read_transcription_md.py | read_order_reference | function | tests/test_repeated_marks.py, utils/lexikon_1910/rescore_passes.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/read_transcription_md.py | read_sheet | function | tests/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.py | grade_sheets | function | (internal use only) |
| utils/lexikon_1910/read_transcription_md.py | report | function | 02_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.py | write | function | tests/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.py | main | function | (internal use only) |
| utils/lexikon_1910/reader_batches.py | read_queue | function | tests/test_batch_wait.py, tests/test_reader_batches.py, tests/test_reader_batches_gemini.py |
| utils/lexikon_1910/reader_batches.py | index_queue | function | tests/test_reader_batches_gemini.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/reader_batches.py | submit | function | 16_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.py | to_native | function | tests/test_reader_batches_gemini.py |
| utils/lexikon_1910/reader_batches.py | check_translation | function | tests/test_reader_batches_gemini.py |
| utils/lexikon_1910/reader_batches.py | collect | function | 16_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.py | gemini_equivalence_submit | function | 15_frozen_run.ipynb, tests/test_reader_batches_gemini.py |
| utils/lexikon_1910/reader_batches.py | gemini_equivalence_collect | function | 15_frozen_run.ipynb, tests/test_reader_batches_gemini.py |
| utils/lexikon_1910/reader_batches.py | line_agreement | function | tests/test_reader_batches_gemini.py |
| utils/lexikon_1910/reader_batches.py | cell_agreement | function | tests/test_reader_batches_gemini.py |
| utils/lexikon_1910/report_unlisted_abbreviations.py | reading_entries | function | (internal use only) |
| utils/lexikon_1910/report_unlisted_abbreviations.py | unlisted | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/report_unlisted_abbreviations.py | main | function | (internal use only) |
| utils/lexikon_1910/rescore_passes.py | out_paths | function | (internal use only) |
| utils/lexikon_1910/rescore_passes.py | write_csv | function | 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.py |
| utils/lexikon_1910/rescore_passes.py | gold_line | function | utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/rescore_passes.py | sidecar | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/rescore_passes.py | score_sidecars | function | (internal use only) |
| utils/lexikon_1910/rescore_passes.py | family_of | function | tests/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.py | order_frames | function | utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/rescore_passes.py | dispute_means | function | utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/rescore_passes.py | order_ledger | function | (internal use only) |
| utils/lexikon_1910/rescore_passes.py | rescore | function | (internal use only) |
| utils/lexikon_1910/rescore_passes.py | main | function | (internal use only) |
| utils/lexikon_1910/run_de_table.py | Arm | class | tests/test_r5_german_notes.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/run_de_table.py | make_reader | function | utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/run_de_table.py | cache_key | function | utils/lexikon_1910/run_lexikon_1910_pilot.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/run_de_table.py | cut | function | tests/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.py | read_blocks | function | tests/test_cache_guard.py, tests/test_de_table.py |
| utils/lexikon_1910/run_de_table.py | keep_carried | function | tests/test_de_table.py |
| utils/lexikon_1910/run_de_table.py | assemble | function | tests/test_de_table.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/run_de_table.py | frozen_pass | function | utils/lexikon_1910/run_frozen.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/run_de_table.py | estimate | function | tests/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.py | pilot_image_cap | function | (internal use only) |
| utils/lexikon_1910/run_de_table.py | sealed_scans | function | tests/test_de_table.py |
| utils/lexikon_1910/run_de_table.py | page_spec | function | tests/test_de_table.py |
| utils/lexikon_1910/run_de_table.py | main | function | (internal use only) |
| utils/lexikon_1910/run_e1_gemini_version.py | label | function | 02_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.py | plan | function | tests/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.py | flags | function | 15_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.py | seed_first_repeat | function | (internal use only) |
| utils/lexikon_1910/run_e1_gemini_version.py | quota_open | function | utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/run_e1_gemini_version.py | wait_for_quota | function | (internal use only) |
| utils/lexikon_1910/run_e1_gemini_version.py | cache_usage | function | utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/run_e1_gemini_version.py | input_tokens | function | tests/test_e1b_candidates.py, utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/run_e1_gemini_version.py | run | function | (internal use only) |
| utils/lexikon_1910/run_e1_gemini_version.py | load_csv | function | (internal use only) |
| utils/lexikon_1910/run_e1_gemini_version.py | v2_claims | function | (internal use only) |
| utils/lexikon_1910/run_e1_gemini_version.py | per_entry | function | (internal use only) |
| utils/lexikon_1910/run_e1_gemini_version.py | pooled | function | 02_data_cleaning.ipynb, utils/docs/build_docs.py, utils/lexikon_1910/lexikon_1910_institutions.py |
| utils/lexikon_1910/run_e1_gemini_version.py | majority | function | utils/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.py | glyph_errors | function | (internal use only) |
| utils/lexikon_1910/run_e1_gemini_version.py | score | function | 16_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.py | main | function | (internal use only) |
| utils/lexikon_1910/run_e1b_second_reader.py | Candidate | class | tests/test_glyph_e3.py, utils/lexikon_1910/locate_glyphs.py |
| utils/lexikon_1910/run_e1b_second_reader.py | label | function | 02_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.py | plan | function | tests/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.py | claude_thinking | function | tests/test_e1b_candidates.py |
| utils/lexikon_1910/run_e1b_second_reader.py | claude_flags | function | (internal use only) |
| utils/lexikon_1910/run_e1b_second_reader.py | flags | function | 15_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.py | estimate | function | tests/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.py | listed | function | (internal use only) |
| utils/lexikon_1910/run_e1b_second_reader.py | ping | function | utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/run_e1b_second_reader.py | ready | function | utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/run_e1b_second_reader.py | run | function | (internal use only) |
| utils/lexikon_1910/run_e1b_second_reader.py | tokens_of | function | (internal use only) |
| utils/lexikon_1910/run_e1b_second_reader.py | family_of | function | tests/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.py | pairs | function | utils/lexikon_1910/build_contrast_sheet.py, utils/parsing/vua_courts.py |
| utils/lexikon_1910/run_e1b_second_reader.py | by_reader | function | (internal use only) |
| utils/lexikon_1910/run_e1b_second_reader.py | order_pairs | function | (internal use only) |
| utils/lexikon_1910/run_e1b_second_reader.py | summary_table | function | 03_visualisation.ipynb, utils/tables/latex_tables.py |
| utils/lexikon_1910/run_e1b_second_reader.py | score | function | 16_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.py | main | function | (internal use only) |
| utils/lexikon_1910/run_e3_pages.py | printed_munis | function | (internal use only) |
| utils/lexikon_1910/run_e3_pages.py | sections_from_index | function | (internal use only) |
| utils/lexikon_1910/run_e3_pages.py | full_locator | function | (internal use only) |
| utils/lexikon_1910/run_e3_pages.py | full_detector | function | (internal use only) |
| utils/lexikon_1910/run_e3_pages.py | Namer | class | 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_e3_pages.py | run | function | (internal use only) |
| utils/lexikon_1910/run_e3_pages.py | rehearse | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_e3_pages.py | main | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | NotReady | class | (internal use only) |
| utils/lexikon_1910/run_frozen.py | editions_for | function | 15_frozen_run.ipynb, tests/test_frozen_run.py, tests/test_lookalike_rules.py |
| utils/lexikon_1910/run_frozen.py | guard | function | tests/test_frozen_run.py, tests/test_ms_sample.py, utils/reference_tools/occupations/czech_glosses.py |
| utils/lexikon_1910/run_frozen.py | finished_certification | function | tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | refuse_finished | function | 15_frozen_run.ipynb, tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | not_wired | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | out_dir | function | 15_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.py | pages_for | function | 15_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.py | page_list | function | 15_frozen_run.ipynb, tests/test_frozen_run.py, utils/reference_tools/places/audit_full_run_pages.py |
| utils/lexikon_1910/run_frozen.py | labels | function | 15_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.py | set_run_aside | function | 15_frozen_run.ipynb, tests/test_lookalike_rules.py |
| utils/lexikon_1910/run_frozen.py | notes_readers | function | 15_frozen_run.ipynb, tests/test_frozen_run.py |
| utils/lexikon_1910/run_frozen.py | actors | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | read_plan | function | 15_frozen_run.ipynb, tests/test_frozen_run.py |
| utils/lexikon_1910/run_frozen.py | queue_root | function | 15_frozen_run.ipynb, tests/test_batch_wait.py |
| utils/lexikon_1910/run_frozen.py | set_reads_aside | function | 15_frozen_run.ipynb, tests/test_frozen_run.py, tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | check_is_current | function | 15_frozen_run.ipynb, tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | read_live | function | — no call site found — |
| utils/lexikon_1910/run_frozen.py | batch_prepare | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | batch_submit | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | batch_collect | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | batch_finish | function | — no call site found — |
| utils/lexikon_1910/run_frozen.py | batch_collect_all | function | 15_frozen_run.ipynb, tests/test_batch_wait.py |
| utils/lexikon_1910/run_frozen.py | wait_for | function | 15_frozen_run.ipynb, tests/test_batch_wait.py |
| utils/lexikon_1910/run_frozen.py | freeze | function | 15_frozen_run.ipynb, tests/test_prefreeze_route.py, utils/lexikon_1910/score_certification.py |
| utils/lexikon_1910/run_frozen.py | gold_manifest_path | function | tests/test_freeze_check.py |
| utils/lexikon_1910/run_frozen.py | freeze_differences | function | tests/test_freeze_check.py, tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | compare_freeze | function | 15_frozen_run.ipynb, tests/test_freeze_check.py |
| utils/lexikon_1910/run_frozen.py | cut_sections | function | tests/test_section_audit.py |
| utils/lexikon_1910/run_frozen.py | sections_missing_offices | function | tests/test_section_audit.py |
| utils/lexikon_1910/run_frozen.py | verdicts_path | function | 15_frozen_run.ipynb, tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | flags_path | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | gate_path | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | run_e3 | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | gold_rows | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | gold_truth | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | dino_rates | function | tests/test_frozen_run.py |
| utils/lexikon_1910/run_frozen.py | e3_gate | function | 15_frozen_run.ipynb, tests/test_frozen_run.py |
| utils/lexikon_1910/run_frozen.py | order_flags | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | point_tb | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | routed_claims | function | tests/test_frozen_run.py |
| utils/lexikon_1910/run_frozen.py | type_claims | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | routed_types | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | final_types | function | tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | build_items | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | repeat_claims | function | tests/test_repeated_marks.py |
| utils/lexikon_1910/run_frozen.py | opus_submit | function | 15_frozen_run.ipynb, tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | opus_collect | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | opus_unanswered | function | 15_frozen_run.ipynb, tests/test_batch_wait.py |
| utils/lexikon_1910/run_frozen.py | opus_estimate | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | ledger_path | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | build_ledger | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | office_claims | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | is_institution | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | final_claims | function | tests/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.py | position_swaps_of | function | tests/test_lookalike_rules.py |
| utils/lexikon_1910/run_frozen.py | swap_counts | function | tests/test_lookalike_rules.py |
| utils/lexikon_1910/run_frozen.py | claim_counts | function | tests/test_prefreeze_route.py, tests/test_route_fixes.py |
| utils/lexikon_1910/run_frozen.py | majority_counts | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | count_for | function | tests/test_route_fixes.py |
| utils/lexikon_1910/run_frozen.py | repeat_less | function | tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | final_inventory | function | tests/test_lookalike_rules.py, tests/test_prefreeze_route.py, tests/test_route_fixes.py, tests/test_section_audit.py |
| utils/lexikon_1910/run_frozen.py | abbreviation_table | function | tests/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.py | printed_reading | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | decoded_rows | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | abbreviation_claims | function | tests/test_abbreviations.py, tests/test_german_plates.py, tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | language_counts | function | tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | entries_read | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | routed_abbreviations | function | tests/test_abbreviations.py |
| utils/lexikon_1910/run_frozen.py | abbreviation_question | function | tests/test_lookalike_rules.py |
| utils/lexikon_1910/run_frozen.py | abbreviation_rulings | function | tests/test_abbreviations.py |
| utils/lexikon_1910/run_frozen.py | abbreviation_rows | function | tests/test_abbreviations.py |
| utils/lexikon_1910/run_frozen.py | final_abbreviations | function | tests/test_abbreviations.py |
| utils/lexikon_1910/run_frozen.py | gold_abbreviations | function | tests/test_abbreviations.py |
| utils/lexikon_1910/run_frozen.py | sheet_abbreviations | function | utils/lexikon_1910/score_certification.py, utils/reference_tools/places/score_sealed_abbreviations.py |
| utils/lexikon_1910/run_frozen.py | score_table | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | score_abbreviations | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | courts_path | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | court_key | function | utils/lexikon_1910/lexikon_1910_register.py |
| utils/lexikon_1910/run_frozen.py | edition_units | function | tests/test_court_key.py |
| utils/lexikon_1910/run_frozen.py | edition_forms | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | anchor_claims | function | tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | without_anchor | function | tests/test_pooled_score.py, utils/lexikon_1910/score_certification.py, utils/validation/score_sealed_retest.py |
| utils/lexikon_1910/run_frozen.py | anchored_keys | function | tests/test_cz_anchor.py |
| utils/lexikon_1910/run_frozen.py | repeated_marks | function | tests/test_repeated_marks.py, utils/lexikon_1910/lexikon_1910_institutions.py |
| utils/lexikon_1910/run_frozen.py | shared_symbols | function | (internal use only) |
| utils/lexikon_1910/run_frozen.py | compare_editions | function | 15_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.py | independence_test | function | 15_frozen_run.ipynb, tests/test_route_fixes.py |
| utils/lexikon_1910/run_frozen.py | score_claims | function | 15_frozen_run.ipynb, tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | sealed_pages | function | tests/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.py | in_sample_pages | function | tests/test_ms_sample.py, tests/test_route_fixes.py |
| utils/lexikon_1910/run_frozen.py | validation_sample | function | 15_frozen_run.ipynb, tests/test_frozen_run.py, tests/test_prefreeze_route.py, tests/test_route_fixes.py |
| utils/lexikon_1910/run_frozen.py | review_queue | function | 15_frozen_run.ipynb |
| utils/lexikon_1910/run_frozen.py | apply_review | function | 15_frozen_run.ipynb, tests/test_frozen_run.py, tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | score_validation | function | 15_frozen_run.ipynb, tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_frozen.py | rehearse | function | utils/lexikon_1910/run_e3_pages.py |
| utils/lexikon_1910/run_glyph_e3.py | Glyph | class | tests/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.py | glyph_at | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | placed_glyphs | function | utils/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.py | cut | function | tests/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.py | crop_white | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/run_glyph_e3.py | cut_all | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | embed | function | utils/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.py | unit | function | 02_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.py | knn_vote | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/run_glyph_e3.py | classify | function | tests/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.py | page_thresholds | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/run_glyph_e3.py | with_abstain | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/run_glyph_e3.py | bank_holds | function | tests/test_glyph_e3.py, utils/reference_tools/places/run_german_oracle.py |
| utils/lexikon_1910/run_glyph_e3.py | evaluate | function | utils/lexikon_1910/run_glyph_test_a.py |
| utils/lexikon_1910/run_glyph_e3.py | type_rows | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | summarise_run | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | name_bases | function | utils/lexikon_1910/run_e3_pages.py |
| utils/lexikon_1910/run_glyph_e3.py | letter_label | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/run_glyph_e3.py | lettered_forms | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | name_letters | function | tests/test_glyph_e3.py, utils/lexikon_1910/run_e3_pages.py |
| utils/lexikon_1910/run_glyph_e3.py | compose_names | function | tests/test_glyph_e3.py, utils/lexikon_1910/run_e3_pages.py |
| utils/lexikon_1910/run_glyph_e3.py | composed_rows | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | letter_rows | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | reader_passes | function | utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_glyph_e3.py | read_reader_claims | function | tests/test_glyph_e3.py, utils/lexikon_1910/run_e3_pages.py |
| utils/lexikon_1910/run_glyph_e3.py | reader_sections | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | iou | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | gold_at | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/run_glyph_e3.py | dispute_rows | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | score_pass | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/run_glyph_e3.py | unclaimed_marks | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/run_glyph_e3.py | file_under | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/run_glyph_e3.py | verdict_of | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/run_glyph_e3.py | section_glyphs | function | utils/lexikon_1910/run_e3_pages.py |
| utils/lexikon_1910/run_glyph_e3.py | verdict_rows | function | tests/test_glyph_e3.py, utils/lexikon_1910/run_e3_pages.py |
| utils/lexikon_1910/run_glyph_e3.py | score_unclaimed | function | tests/test_glyph_e3.py |
| utils/lexikon_1910/run_glyph_e3.py | score_readers | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | write_csv | function | utils/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.py | report | function | 02_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.py | composed_report | function | (internal use only) |
| utils/lexikon_1910/run_glyph_e3.py | main | function | (internal use only) |
| utils/lexikon_1910/run_glyph_test_a.py | square | function | utils/lexikon_1910/run_glyph_e3.py |
| utils/lexikon_1910/run_glyph_test_a.py | pixel_features | function | utils/lexikon_1910/run_glyph_e3.py |
| utils/lexikon_1910/run_glyph_test_a.py | pixel_values | function | (internal use only) |
| utils/lexikon_1910/run_glyph_test_a.py | encoder_features | function | utils/lexikon_1910/run_glyph_e3.py |
| utils/lexikon_1910/run_glyph_test_a.py | leave_page_out | function | (internal use only) |
| utils/lexikon_1910/run_glyph_test_a.py | evaluate | function | utils/lexikon_1910/run_glyph_e3.py |
| utils/lexikon_1910/run_glyph_test_a.py | main | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | encode_png | function | 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/lexikon_1910/run_lexikon_1910_pilot.py | key_image_digest | function | tests/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.py | key_image_cache | function | tests/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.py | Truncated | class | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | RateLimited | class | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | ClaudeReader | class | tests/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.py | OpenAICompatibleReader | class | tests/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.py | OllamaReader | class | — no call site found — |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | build_reader | function | utils/lexikon_1910/lexikon_1910_pages.py |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | block_parts | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | build_notes_reader | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | parse_price | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | table_rows | function | tests/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.py | rows_to_block | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | compare_channels | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | parse_pages | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | write_json | function | utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | write_csv | function | utils/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.py | probe_reader | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | heading_reader | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | printed_line_count | function | tests/test_lexikon_1910_pilot_checks.py, utils/lexikon_1910/run_r5_german_notes.py |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | crop_audit | function | tests/test_lexikon_1910_pilot_checks.py |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | image_cut_report | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | legend_pass | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | apparatus_pass | function | (internal use only) |
| utils/lexikon_1910/run_lexikon_1910_pilot.py | main | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | Arm | class | tests/test_r5_german_notes.py, utils/lexikon_1910/run_de_table.py |
| utils/lexikon_1910/run_r5_german_notes.py | label | function | 02_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.py | is_done | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | plan | function | tests/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.py | gold_pages | function | 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_frozen.py, utils/lexikon_1910/run_table_arm.py |
| utils/lexikon_1910/run_r5_german_notes.py | make_reader | function | utils/lexikon_1910/run_de_table.py |
| utils/lexikon_1910/run_r5_german_notes.py | systems | function | tests/test_de_table.py, utils/reference_tools/places/run_german_reader_boxes.py |
| utils/lexikon_1910/run_r5_german_notes.py | cache_key | function | utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_lexikon_1910_pilot.py |
| utils/lexikon_1910/run_r5_german_notes.py | cached_line_reader | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | PageCut | class | — no call site found — |
| utils/lexikon_1910/run_r5_german_notes.py | carry_into | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | save_sections | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | cut_pages | function | utils/reference_tools/places/audit_full_run_pages.py |
| utils/lexikon_1910/run_r5_german_notes.py | jobs_of | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | read_crops | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | clean_lines | function | tests/test_r5_german_notes.py |
| utils/lexikon_1910/run_r5_german_notes.py | legend_rows | function | tests/test_r5_german_notes.py |
| utils/lexikon_1910/run_r5_german_notes.py | audit_rows | function | tests/test_r5_german_notes.py |
| utils/lexikon_1910/run_r5_german_notes.py | assemble | function | tests/test_de_table.py, utils/lexikon_1910/run_de_table.py |
| utils/lexikon_1910/run_r5_german_notes.py | run_pass | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | frozen_pass | function | utils/lexikon_1910/run_de_table.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_r5_german_notes.py | header_rows_of | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | heading_rows | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | cost | function | utils/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.py | record_timing | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | report_pass | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | park | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | image_tokens | function | tests/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.py | estimate | function | tests/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.py | dry_run | function | utils/lexikon_1910/run_verify_test_b.py, utils/scraping/run_matice_downloads.py |
| utils/lexikon_1910/run_r5_german_notes.py | ready | function | utils/lexikon_1910/run_e1b_second_reader.py |
| utils/lexikon_1910/run_r5_german_notes.py | run | function | (internal use only) |
| utils/lexikon_1910/run_r5_german_notes.py | main | function | (internal use only) |
| utils/lexikon_1910/run_table_arm.py | label | function | 02_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.py | rows_file | function | (internal use only) |
| utils/lexikon_1910/run_table_arm.py | read_by_model | function | (internal use only) |
| utils/lexikon_1910/run_table_arm.py | plan | function | tests/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.py | flags | function | 15_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.py | run | function | (internal use only) |
| utils/lexikon_1910/run_table_arm.py | main | function | (internal use only) |
| utils/lexikon_1910/run_verify_test_b.py | set_arm | function | (internal use only) |
| utils/lexikon_1910/run_verify_test_b.py | pass_rows | function | utils/lexikon_1910/build_mark_ledger.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | claim_sets | function | tests/test_verify_test_b.py |
| utils/lexikon_1910/run_verify_test_b.py | disputes | function | tests/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.py | flagged | function | tests/test_verify_test_b.py, utils/lexikon_1910/lexikon_1910_ledger.py, utils/parsing/vua_courts.py |
| utils/lexikon_1910/run_verify_test_b.py | unit_of | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | unit_id | function | tests/test_abbreviations.py, tests/test_verify_test_b.py |
| utils/lexikon_1910/run_verify_test_b.py | place_names | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | load_parts | function | (internal use only) |
| utils/lexikon_1910/run_verify_test_b.py | whose_options | function | tests/test_verify_test_b.py |
| utils/lexikon_1910/run_verify_test_b.py | load_bases | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | confusions | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | partners | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | options_for | function | tests/test_verify_test_b.py |
| utils/lexikon_1910/run_verify_test_b.py | load_geometry | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | image_path | function | tests/test_opus_cards.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | gold_image_path | function | tests/test_opus_cards.py |
| utils/lexikon_1910/run_verify_test_b.py | band_of | function | utils/parsing/ranks.py |
| utils/lexikon_1910/run_verify_test_b.py | key_starts | function | (internal use only) |
| utils/lexikon_1910/run_verify_test_b.py | entry_bands | function | (internal use only) |
| utils/lexikon_1910/run_verify_test_b.py | marks_of | function | tests/test_abbreviations.py |
| utils/lexikon_1910/run_verify_test_b.py | reference_card | function | tests/test_opus_cards.py |
| utils/lexikon_1910/run_verify_test_b.py | image_digest | function | tests/test_opus_cards.py, tests/test_prefreeze_route.py |
| utils/lexikon_1910/run_verify_test_b.py | saved_image_digest | function | tests/test_opus_cards.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | build_item | function | tests/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.py | schema_for | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | system_text | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | params_for | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | image_tokens | function | tests/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.py | build_all | function | (internal use only) |
| utils/lexikon_1910/run_verify_test_b.py | dry_run | function | utils/lexikon_1910/run_r5_german_notes.py, utils/scraping/run_matice_downloads.py |
| utils/lexikon_1910/run_verify_test_b.py | write_items | function | tests/test_opus_cards.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | sent_path | function | tests/test_opus_cards.py |
| utils/lexikon_1910/run_verify_test_b.py | record_sent | function | tests/test_opus_cards.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/run_verify_test_b.py | unmatched_path | function | tests/test_opus_cards.py |
| utils/lexikon_1910/run_verify_test_b.py | submit | function | 16_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.py | collect | function | 16_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.py | claim_verdicts | function | tests/test_verify_test_b.py |
| utils/lexikon_1910/run_verify_test_b.py | measures | function | tests/test_verify_test_b.py |
| utils/lexikon_1910/run_verify_test_b.py | flagged_measures | function | (internal use only) |
| utils/lexikon_1910/run_verify_test_b.py | recovered | function | (internal use only) |
| utils/lexikon_1910/run_verify_test_b.py | whose_rulings | function | tests/test_verify_test_b.py |
| utils/lexikon_1910/run_verify_test_b.py | gain_interval | function | (internal use only) |
| utils/lexikon_1910/run_verify_test_b.py | bill | function | tests/test_verify_test_b.py |
| utils/lexikon_1910/run_verify_test_b.py | score | function | 16_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.py | main | function | (internal use only) |
| utils/lexikon_1910/score_certification.py | sealed_paths | function | tests/test_ms_sample.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/score_sealed_retest.py |
| utils/lexikon_1910/score_certification.py | sheet_status | function | utils/validation/score_sealed_retest.py |
| utils/lexikon_1910/score_certification.py | sealed_sheets | function | utils/reference_tools/places/score_sealed_abbreviations.py |
| utils/lexikon_1910/score_certification.py | sealed_rows | function | tests/test_german_plates.py |
| utils/lexikon_1910/score_certification.py | printed_entries | function | tests/test_sealed_scorer.py, utils/reference_tools/places/score_sealed_abbreviations.py |
| utils/lexikon_1910/score_certification.py | court_of | function | utils/lexikon_1910/lexikon_1910_notes.py, utils/reference_tools/places/score_sealed_abbreviations.py |
| utils/lexikon_1910/score_certification.py | claims_of | function | tests/test_abbreviations.py |
| utils/lexikon_1910/score_certification.py | route_claims | function | (internal use only) |
| utils/lexikon_1910/score_certification.py | prf | function | (internal use only) |
| utils/lexikon_1910/score_certification.py | score_sets | function | tests/test_religion_categories.py, utils/reference_tools/places/score_sealed_abbreviations.py |
| utils/lexikon_1910/score_certification.py | page_clustered_interval | function | utils/reference_tools/places/score_sealed_abbreviations.py |
| utils/lexikon_1910/score_certification.py | entry_exact | function | (internal use only) |
| utils/lexikon_1910/score_certification.py | count_accuracy | function | utils/reference_tools/places/score_sealed_abbreviations.py |
| utils/lexikon_1910/score_certification.py | per_symbol | function | tests/test_sealed_scorer.py, utils/reference_tools/places/score_sealed_abbreviations.py |
| utils/lexikon_1910/score_certification.py | author_dependent | function | tests/test_religion_categories.py |
| utils/lexikon_1910/score_certification.py | freeze_record | function | utils/validation/score_sealed_retest.py |
| utils/lexikon_1910/score_certification.py | freeze_refusal | function | (internal use only) |
| utils/lexikon_1910/score_certification.py | without_anchor | function | tests/test_pooled_score.py, utils/lexikon_1910/run_frozen.py, utils/validation/score_sealed_retest.py |
| utils/lexikon_1910/score_certification.py | score | function | 16_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.py | gold_cooperatives | function | tests/test_pooled_score.py |
| utils/lexikon_1910/score_certification.py | route_cooperatives | function | tests/test_pooled_score.py |
| utils/lexikon_1910/score_certification.py | sides | function | tests/test_sealed_scorer.py, utils/reference_tools/places/score_sealed_abbreviations.py, utils/validation/score_sealed_retest.py |
| utils/lexikon_1910/score_certification.py | merge | function | 02_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.py | by_category | function | tests/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.py | waiting_marks | function | utils/validation/score_sealed_retest.py |
| utils/lexikon_1910/score_certification.py | measure | function | tests/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.py | score_pooled | function | tests/test_sealed_scorer.py |
| utils/lexikon_1910/score_certification.py | report | function | 02_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.py | main | function | (internal use only) |
| utils/lexikon_1910/score_r5_german_notes.py | family_of | function | tests/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.py | abbreviations | function | (internal use only) |
| utils/lexikon_1910/score_r5_german_notes.py | sheet_paths | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/score_r5_german_notes.py | as_written | function | tests/test_r5_german_notes.py |
| utils/lexikon_1910/score_r5_german_notes.py | read_gold | function | 16_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.py | ledger_names | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/score_r5_german_notes.py | arm_summary | function | (internal use only) |
| utils/lexikon_1910/score_r5_german_notes.py | side_files | function | (internal use only) |
| utils/lexikon_1910/score_r5_german_notes.py | legend_agreement | function | (internal use only) |
| utils/lexikon_1910/score_r5_german_notes.py | side_lines | function | (internal use only) |
| utils/lexikon_1910/score_r5_german_notes.py | score_german | function | (internal use only) |
| utils/lexikon_1910/score_r5_german_notes.py | matched_cz_scans | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/score_r5_german_notes.py | compare | function | tests/test_statutory_institutions.py, utils/reference_tools/places/statutory_institutions.py |
| utils/lexikon_1910/score_r5_german_notes.py | write_csv | function | utils/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.py | main | function | (internal use only) |
| utils/lexikon_1910/score_table_passes.py | norm_name | function | tests/test_score_table_passes.py |
| utils/lexikon_1910/score_table_passes.py | norm_name_strict | function | tests/test_score_table_passes.py |
| utils/lexikon_1910/score_table_passes.py | norm_number | function | tests/test_score_table_passes.py |
| utils/lexikon_1910/score_table_passes.py | norm_text | function | tests/test_score_table_passes.py |
| utils/lexikon_1910/score_table_passes.py | norm | function | tests/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.py | load_gold | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/score_table_passes.py | load_rows | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/score_table_passes.py | label_of | function | utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/score_table_passes.py | pair_score | function | (internal use only) |
| utils/lexikon_1910/score_table_passes.py | align | function | tests/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.py | readings | function | 16_theatre_model_pass.ipynb |
| utils/lexikon_1910/score_table_passes.py | score_reading | function | tests/test_score_table_passes.py |
| utils/lexikon_1910/score_table_passes.py | cross | function | (internal use only) |
| utils/lexikon_1910/score_table_passes.py | fmt | function | utils/docs/build_docs.py, utils/validation/build_validation_workbook.py |
| utils/lexikon_1910/score_table_passes.py | report | function | 02_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.py | pass_files | function | (internal use only) |
| utils/lexikon_1910/score_table_passes.py | main | function | (internal use only) |
| utils/lexikon_1910/section_images.py | name_for | function | (internal use only) |
| utils/lexikon_1910/section_images.py | save | function | tests/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.py | read_index | function | tests/test_frozen_run.py, utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py |
| utils/lexikon_1910/section_images.py | index_from_names | function | utils/lexikon_1910/run_e3_pages.py, utils/lexikon_1910/run_frozen.py |
| utils/linkage.py | phonetic_key | function | utils/parsing/vua_courts.py |
| utils/linkage.py | names_compatible | function | utils/parsing/vua_courts.py, utils/validation/data_checks.py |
| utils/linkage.py | find_overlap | function | 02_data_cleaning.ipynb |
| utils/linkage.py | add_overlap_flags | function | 02_data_cleaning.ipynb |
| utils/linkage.py | add_duplicate_groups | function | 02_data_cleaning.ipynb |
| utils/linkage.py | linkage_selftest | function | utils/validation/data_checks.py |
| utils/linkage.py | linkage_report | function | 02_data_cleaning.ipynb |
| utils/parsing/affiliations.py | parse_affiliation | function | (internal use only) |
| utils/parsing/affiliations.py | atlas_codes | function | (internal use only) |
| utils/parsing/affiliations.py | affiliation_selftest | function | utils/validation/data_checks.py |
| utils/parsing/affiliations.py | add_affiliation_columns | function | 02_data_cleaning.ipynb |
| utils/parsing/affiliations.py | affiliation_report | function | 02_data_cleaning.ipynb |
| utils/parsing/amateur_theatre.py | normalise_name | function | tests/test_amateur_theatre.py, utils/lexikon_1910/lexikon_1910_de.py, utils/lexikon_1910/lexikon_1910_pages.py |
| utils/parsing/amateur_theatre.py | city_part_name | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | strip_diacritics | function | (internal use only) |
| utils/parsing/amateur_theatre.py | haversine_km | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | okres_tokens | function | — no call site found — |
| utils/parsing/amateur_theatre.py | settlement_frame | function | tests/test_amateur_theatre.py, utils/docs/build_docs.py, utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | join_places_to_settlements | function | tests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | starts_before | function | tests/test_amateur_theatre.py, utils/scraping/scraper_amaterskedivadlo.py |
| utils/parsing/amateur_theatre.py | first_record_period | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | place_dates | function | (internal use only) |
| utils/parsing/amateur_theatre.py | classify_association | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | association_counts | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | year_span | function | utils/parsing/amateur_theatre_model.py |
| utils/parsing/amateur_theatre.py | infer_century | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | parse_leading_date | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | attests_before | function | tests/test_amateur_theatre.py, utils/parsing/amateur_theatre_model.py |
| utils/parsing/amateur_theatre.py | chronicle_line_kind | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | read_chronicle | function | tests/test_amateur_theatre_model.py, utils/parsing/amateur_theatre_model.py |
| utils/parsing/amateur_theatre.py | chronicle_lines | function | tests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | chronicle_events | function | (internal use only) |
| utils/parsing/amateur_theatre.py | chronicle_place_summary | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | century_inference_check | function | (internal use only) |
| utils/parsing/amateur_theatre.py | chronicle_report | function | utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | is_report | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | clause_cuts | function | (internal use only) |
| utils/parsing/amateur_theatre.py | split_clauses | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | chronicle_clauses | function | tests/test_amateur_theatre.py, tests/test_amateur_theatre_model.py, utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | event_cues | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | cue_table | function | utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | clause_report | function | utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | code_clause | function | tests/test_amateur_theatre.py |
| utils/parsing/amateur_theatre.py | code_clauses | function | tests/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.py | active_decade_list | function | — no call site found — |
| utils/parsing/amateur_theatre.py | ensemble_chronicle_clauses | function | tests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | assign_companies | function | (internal use only) |
| utils/parsing/amateur_theatre.py | evidence_table | function | tests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | year_bounds | function | tests/test_amateur_theatre.py, utils/docs/build_docs.py |
| utils/parsing/amateur_theatre.py | place_year_panel | function | tests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | area_year_tables | function | tests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | panel_report | function | utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | ensemble_page_summary | function | tests/test_amateur_theatre.py, utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | coverage_tables | function | utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre.py | country_tables | function | (internal use only) |
| utils/parsing/amateur_theatre.py | coverage_report | function | utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre_model.py | clause_keys | function | tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | load_clauses | function | 16_theatre_model_pass.ipynb |
| utils/parsing/amateur_theatre_model.py | select_open | function | 16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | open_counts | function | 16_theatre_model_pass.ipynb |
| utils/parsing/amateur_theatre_model.py | glossary_patterns | function | tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | glossary | function | tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | add_context | function | 16_theatre_model_pass.ipynb |
| utils/parsing/amateur_theatre_model.py | clause_message | function | 16_theatre_model_pass.ipynb |
| utils/parsing/amateur_theatre_model.py | build_request | function | tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | request_key | function | tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | build_queue | function | 16_theatre_model_pass.ipynb |
| utils/parsing/amateur_theatre_model.py | estimate_cost | function | 16_theatre_model_pass.ipynb |
| utils/parsing/amateur_theatre_model.py | usage_cost | function | tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | read_state | function | (internal use only) |
| utils/parsing/amateur_theatre_model.py | submit | function | 16_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.py | parse_reply | function | tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | collect | function | 16_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.py | reading_problems | function | tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | readings_table | function | 16_theatre_model_pass.ipynb |
| utils/parsing/amateur_theatre_model.py | cost_report | function | 16_theatre_model_pass.ipynb |
| utils/parsing/amateur_theatre_model.py | apply_readings | function | tests/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.py | load_readings | function | utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/parsing/amateur_theatre_model.py | write_readings | function | 16_theatre_model_pass.ipynb |
| utils/parsing/amateur_theatre_model.py | draw_gold_sample | function | 16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | write_gold_sheet | function | 16_theatre_model_pass.ipynb, tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | fill_gold_sheet | function | tests/test_amateur_theatre_model.py |
| utils/parsing/amateur_theatre_model.py | read_gold | function | 16_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.py | score | function | 16_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.py | load_census_file | function | utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/census.py | build_census_panel | function | (internal use only) |
| utils/parsing/census.py | okres_census_crosswalk | function | 02_data_cleaning.ipynb |
| utils/parsing/census.py | pool_twin_okresy | function | (internal use only) |
| utils/parsing/census.py | blank_twin_members | function | (internal use only) |
| utils/parsing/census.py | build_census_panels | function | 02_data_cleaning.ipynb |
| utils/parsing/census.py | add_census_okres_id | function | 02_data_cleaning.ipynb |
| utils/parsing/census.py | census_report | function | 02_data_cleaning.ipynb |
| utils/parsing/census_hu.py | build_census_hu | function | 02_data_cleaning.ipynb |
| utils/parsing/census_hu.py | census_hu_report | function | 02_data_cleaning.ipynb |
| utils/parsing/census_labels.py | english_rename_map | function | utils/parsing/census.py |
| utils/parsing/census_labels.py | add_shares_cis | function | utils/parsing/census.py |
| utils/parsing/census_labels.py | add_shares_hu | function | (internal use only) |
| utils/parsing/census_labels.py | to_english_census | function | utils/parsing/census.py, utils/parsing/census_hu.py |
| utils/parsing/clean.py | normalize_missing | function | 02_data_cleaning.ipynb |
| utils/parsing/clean.py | clean_names | function | 02_data_cleaning.ipynb |
| utils/parsing/clean.py | strip_place_markers | function | — no call site found — |
| utils/parsing/clean.py | clean_places | function | 02_data_cleaning.ipynb |
| utils/parsing/clean.py | clean_dataframe | function | 02_data_cleaning.ipynb |
| utils/parsing/clean.py | clean_report | function | 02_data_cleaning.ipynb |
| utils/parsing/dates.py | parse_date | function | utils/parsing/exit_codes.py |
| utils/parsing/dates.py | add_date_columns | function | 02_data_cleaning.ipynb, utils/parsing/habsburg.py |
| utils/parsing/dates.py | date_report | function | 02_data_cleaning.ipynb, utils/parsing/habsburg.py |
| utils/parsing/districts.py | read_dbf | function | utils/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.py | parse_mpidr | function | utils/reference_tools/places/build_curation.py |
| utils/parsing/districts.py | attach_gazetteer_names | function | — no call site found — |
| utils/parsing/districts.py | build_district_backbone | function | 02_data_cleaning.ipynb |
| utils/parsing/districts.py | write_curation_template | function | — no call site found — |
| utils/parsing/districts.py | districts_report | function | 02_data_cleaning.ipynb |
| utils/parsing/districts.py | validate_against_code | function | 02_data_cleaning.ipynb |
| utils/parsing/districts_hu.py | build_jaras_backbone | function | 02_data_cleaning.ipynb, utils/reference_tools/places/build_sk_curation.py, utils/validation/build_validation_workbook.py |
| utils/parsing/districts_hu.py | write_sk_curation_template | function | — no call site found — |
| utils/parsing/districts_hu.py | jaras_report | function | 02_data_cleaning.ipynb, utils/parsing/records_geo_hu.py |
| utils/parsing/education.py | parse_education | function | utils/reference_tools/education/draw_education_sample.py, utils/reference_tools/education/score_education_gold.py |
| utils/parsing/education.py | clean_education | function | — no call site found — |
| utils/parsing/education.py | add_education_column | function | 02_data_cleaning.ipynb |
| utils/parsing/education.py | education_selftest | function | utils/validation/data_checks.py |
| utils/parsing/education.py | ladder_report | function | — no call site found — |
| utils/parsing/education.py | education_report | function | 02_data_cleaning.ipynb |
| utils/parsing/exit_codes.py | parse_exit_codes | function | utils/validation/data_checks.py |
| utils/parsing/exit_codes.py | label_exit_codes | function | — no call site found — |
| utils/parsing/exit_codes.py | add_exit_columns | function | 02_data_cleaning.ipynb, utils/tables/latex_tables.py |
| utils/parsing/exit_codes.py | parse_exit_date | function | — no call site found — |
| utils/parsing/exit_codes.py | exit_code_report | function | 02_data_cleaning.ipynb |
| utils/parsing/geonames.py | gpkg_point | function | tests/test_ruian.py |
| utils/parsing/geonames.py | read_geonames | function | utils/parsing/ruian.py |
| utils/parsing/geonames.py | geonames_report | function | utils/parsing/ruian.py |
| utils/parsing/habsburg.py | parse_cause | function | — no call site found — |
| utils/parsing/habsburg.py | add_cause_columns | function | (internal use only) |
| utils/parsing/habsburg.py | cause_report | function | (internal use only) |
| utils/parsing/habsburg.py | extract_theatre | function | — no call site found — |
| utils/parsing/habsburg.py | add_theatre_column | function | (internal use only) |
| utils/parsing/habsburg.py | theatre_report | function | (internal use only) |
| utils/parsing/habsburg.py | parse_ah_units | function | — no call site found — |
| utils/parsing/habsburg.py | add_ah_unit_columns | function | (internal use only) |
| utils/parsing/habsburg.py | ah_unit_report | function | 03_visualisation.ipynb |
| utils/parsing/habsburg.py | habsburg_selftest | function | utils/validation/data_checks.py |
| utils/parsing/habsburg.py | add_note_columns | function | (internal use only) |
| utils/parsing/habsburg.py | note_report | function | 03_visualisation.ipynb |
| utils/parsing/habsburg.py | add_habsburg_columns | function | 02_data_cleaning.ipynb |
| utils/parsing/habsburg.py | habsburg_report | function | 02_data_cleaning.ipynb |
| utils/parsing/lexikon.py | settlement_slug | function | — no call site found — |
| utils/parsing/lexikon.py | add_settlement_id | function | tests/test_register_build.py |
| utils/parsing/lexikon.py | parse_lexikon | function | (internal use only) |
| utils/parsing/lexikon.py | build_settlement_crosswalk | function | 02_data_cleaning.ipynb |
| utils/parsing/lexikon.py | lexikon_report | function | 02_data_cleaning.ipynb |
| utils/parsing/lexikon_sk.py | parse_lexikon_sk | function | (internal use only) |
| utils/parsing/lexikon_sk.py | build_crosswalk_sk | function | 02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py |
| utils/parsing/lexikon_sk.py | lexikon_sk_report | function | 02_data_cleaning.ipynb |
| utils/parsing/locations.py | parse_location | function | — no call site found — |
| utils/parsing/locations.py | add_location_columns | function | utils/parsing/habsburg.py |
| utils/parsing/locations.py | location_report | function | 03_visualisation.ipynb, utils/parsing/habsburg.py |
| utils/parsing/locative.py | norm | function | tests/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.py | noun_locatives | function | — no call site found — |
| utils/parsing/locative.py | adjective_locatives | function | — no call site found — |
| utils/parsing/locative.py | noun_genitives | function | — no call site found — |
| utils/parsing/locative.py | adjective_genitives | function | — no call site found — |
| utils/parsing/locative.py | locative_keys | function | tests/test_locative.py |
| utils/parsing/locative.py | czech_spellings | function | tests/test_locative.py |
| utils/parsing/locative.py | short_name | function | (internal use only) |
| utils/parsing/locative.py | build_index | function | tests/test_locative.py, utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/locative.py | split_residence | function | tests/test_locative.py, utils/parsing/matice.py |
| utils/parsing/locative.py | fuzzy_key | function | tests/test_locative.py |
| utils/parsing/locative.py | qualifier_point | function | tests/test_locative.py |
| utils/parsing/locative.py | near_candidates | function | (internal use only) |
| utils/parsing/locative.py | fuzzy_near | function | (internal use only) |
| utils/parsing/locative.py | match_residence | function | tests/test_locative.py |
| utils/parsing/locative.py | decide | function | tests/test_locative.py |
| utils/parsing/locative.py | last_word_index | function | tests/test_locative.py |
| utils/parsing/locative.py | match_all | function | utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/matice.py | normalise | function | tests/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.py | alto_lines | function | (internal use only) |
| utils/parsing/matice.py | read_pdf_lines | function | (internal use only) |
| utils/parsing/matice.py | read_lines | function | (internal use only) |
| utils/parsing/matice.py | assign_sections | function | (internal use only) |
| utils/parsing/matice.py | collapse_spacing | function | tests/test_matice.py |
| utils/parsing/matice.py | split_residence | function | tests/test_locative.py, utils/parsing/locative.py |
| utils/parsing/matice.py | is_corporate | function | (internal use only) |
| utils/parsing/matice.py | parse_person | function | tests/test_matice.py |
| utils/parsing/matice.py | surname_of | function | tests/test_matice.py |
| utils/parsing/matice.py | roman_to_int | function | tests/test_matice.py |
| utils/parsing/matice.py | int_to_roman | function | (internal use only) |
| utils/parsing/matice.py | mc_complete | function | (internal use only) |
| utils/parsing/matice.py | assemble_mc | function | (internal use only) |
| utils/parsing/matice.py | parse_mc | function | tests/test_matice.py |
| utils/parsing/matice.py | resolve_ditto | function | tests/test_matice.py |
| utils/parsing/matice.py | join_lines | function | (internal use only) |
| utils/parsing/matice.py | split_run_on | function | tests/test_matice.py |
| utils/parsing/matice.py | split_by_place | function | tests/test_matice.py |
| utils/parsing/matice.py | column_margins | function | tests/test_matice.py |
| utils/parsing/matice.py | assemble_lines | function | (internal use only) |
| utils/parsing/matice.py | paid_year_segments | function | tests/test_matice.py |
| utils/parsing/matice.py | parse_source | function | (internal use only) |
| utils/parsing/matice.py | parse_all | function | utils/parsing/vua_courts.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/units/run_vua_courts_pilot.py |
| utils/parsing/matice.py | totals_report | function | utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/matice.py | mc_number_order | function | utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/matice.py | entry_year_from_number | function | utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/matice.py | occupation_class | function | tests/test_matice.py, utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/matice.py | member_group | function | tests/test_matice.py |
| utils/parsing/matice.py | mc_founders | function | tests/test_matice.py |
| utils/parsing/matice.py | founder_key | function | utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/matice.py | eb_rate | function | tests/test_matice.py, utils/reference_tools/places/run_intensities.py |
| utils/parsing/matice.py | measure_groups | function | utils/reference_tools/places/map_matice.py, utils/reference_tools/places/run_intensities.py |
| utils/parsing/matice.py | okres_measure | function | utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/matice.py | own_society_z | function | tests/test_matice.py |
| utils/parsing/matice.py | own_society_czech | function | tests/test_matice.py |
| utils/parsing/matice.py | add_own_society | function | tests/test_matice.py |
| utils/parsing/matice.py | census_court_pool | function | utils/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.py | court_measure | function | utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/matice.py | month_heading | function | tests/test_matice.py |
| utils/parsing/matice.py | payment_amount | function | tests/test_matice.py |
| utils/parsing/matice.py | parse_payer | function | tests/test_matice.py |
| utils/parsing/matice.py | read_text_lines | function | (internal use only) |
| utils/parsing/matice.py | slice_lines | function | (internal use only) |
| utils/parsing/matice.py | month_blocks | function | tests/test_matice.py |
| utils/parsing/matice.py | block_payments | function | tests/test_matice.py |
| utils/parsing/matice.py | parse_payments | function | utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/matice.py | block_name_order | function | tests/test_matice.py |
| utils/parsing/matice.py | month_check | function | (internal use only) |
| utils/parsing/matice.py | payer_key | function | tests/test_matice.py |
| utils/parsing/matice.py | borrow_places | function | tests/test_matice.py, utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/matice.py | payment_persons | function | tests/test_matice.py, utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/matice.py | joiners | function | tests/test_matice.py, utils/reference_tools/places/run_matice_pilot.py |
| utils/parsing/occupations.py | load_hisco_authority | function | utils/reference_tools/occupations/build_conflict_workbook.py, utils/validation/data_checks.py |
| utils/parsing/occupations.py | load_occupation_curation | function | (internal use only) |
| utils/parsing/occupations.py | split_occupations | function | (internal use only) |
| utils/parsing/occupations.py | extract_qualifier | function | (internal use only) |
| utils/parsing/occupations.py | extract_career_stage | function | (internal use only) |
| utils/parsing/occupations.py | code_occupation | function | utils/docs/build_docs.py, utils/validation/data_checks.py |
| utils/parsing/occupations.py | add_occupation_columns | function | 02_data_cleaning.ipynb |
| utils/parsing/occupations.py | matcher_selftest | function | utils/validation/data_checks.py |
| utils/parsing/occupations.py | occupation_report | function | 02_data_cleaning.ipynb |
| utils/parsing/prng.py | parse_dms | function | tests/test_ruian.py |
| utils/parsing/prng.py | read_prng | function | utils/parsing/ruian.py |
| utils/parsing/prng.py | silesia_okres_ids | function | utils/parsing/ruian.py |
| utils/parsing/prng.py | add_court_1910 | function | utils/parsing/ruian.py |
| utils/parsing/prng.py | prng_report | function | utils/parsing/ruian.py |
| utils/parsing/provenance.py | add_provenance_columns | function | 02_data_cleaning.ipynb |
| utils/parsing/provenance.py | provenance_report | function | 02_data_cleaning.ipynb |
| utils/parsing/provenance.py | provenance_selftest | function | utils/validation/data_checks.py |
| utils/parsing/ranks.py | clean_rank | function | utils/validation/data_checks.py |
| utils/parsing/ranks.py | english_label | function | — no call site found — |
| utils/parsing/ranks.py | resolve_ordinals | function | — no call site found — |
| utils/parsing/ranks.py | branch_of | function | — no call site found — |
| utils/parsing/ranks.py | common_grade | function | — no call site found — |
| utils/parsing/ranks.py | band_of | function | utils/lexikon_1910/run_verify_test_b.py |
| utils/parsing/ranks.py | add_rank_columns | function | 02_data_cleaning.ipynb |
| utils/parsing/ranks.py | add_rank_reset | function | 02_data_cleaning.ipynb |
| utils/parsing/ranks.py | rank_report | function | 02_data_cleaning.ipynb |
| utils/parsing/records_geo.py | settlement_name_set | function | 02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py |
| utils/parsing/records_geo.py | build_okres_lookup | function | (internal use only) |
| utils/parsing/records_geo.py | add_birth_okres_id | function | utils/parsing/lexikon.py |
| utils/parsing/records_geo.py | add_birth_settlement_okres | function | (internal use only) |
| utils/parsing/records_geo.py | build_twin_pairs | function | 02_data_cleaning.ipynb, utils/docs/build_docs.py, utils/parsing/census.py, utils/validation/data_checks.py |
| utils/parsing/records_geo.py | resolve_twin_pairs | function | (internal use only) |
| utils/parsing/records_geo.py | strip_praha | function | (internal use only) |
| utils/parsing/records_geo.py | load_prague_suburbs | function | utils/validation/data_checks.py |
| utils/parsing/records_geo.py | resolve_prague_suburbs | function | 02_data_cleaning.ipynb |
| utils/parsing/records_geo.py | prague_suburb_report | function | 02_data_cleaning.ipynb |
| utils/parsing/records_geo.py | twin_pair_report | function | 02_data_cleaning.ipynb |
| utils/parsing/records_geo.py | birth_okres_report | function | 02_data_cleaning.ipynb |
| utils/parsing/records_geo.py | sync_cisleithanian | function | utils/parsing/records_geo_hu.py |
| utils/parsing/records_geo.py | settlement_homonyms | function | tests/test_place_matching_precision.py |
| utils/parsing/records_geo.py | add_settlement_attrs | function | tests/test_place_matching_precision.py |
| utils/parsing/records_geo.py | add_okres_names | function | (internal use only) |
| utils/parsing/records_geo.py | add_land | function | (internal use only) |
| utils/parsing/records_geo.py | add_place_standardization | function | 02_data_cleaning.ipynb |
| utils/parsing/records_geo_hu.py | add_jaras_id | function | 02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py |
| utils/parsing/records_geo_hu.py | add_settlement_jaras | function | 02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py |
| utils/parsing/records_geo_hu.py | build_jaras_twin_pairs | function | 02_data_cleaning.ipynb, utils/validation/data_checks.py |
| utils/parsing/records_geo_hu.py | resolve_jaras_twins | function | 02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py |
| utils/parsing/records_geo_hu.py | set_land_from_jaras | function | 02_data_cleaning.ipynb, utils/validation/build_validation_workbook.py |
| utils/parsing/records_geo_hu.py | records_geo_hu_selftest | function | utils/validation/data_checks.py |
| utils/parsing/records_geo_hu.py | jaras_twin_report | function | 02_data_cleaning.ipynb |
| utils/parsing/records_geo_hu.py | jaras_report | function | 02_data_cleaning.ipynb, utils/parsing/districts_hu.py |
| utils/parsing/religion.py | translate_religion | function | — no call site found — |
| utils/parsing/religion.py | add_religion_en | function | 02_data_cleaning.ipynb |
| utils/parsing/religion.py | religion_report | function | 02_data_cleaning.ipynb |
| utils/parsing/ruian.py | krovak_inverse | function | tests/test_ruian.py |
| utils/parsing/ruian.py | krovak_forward | function | tests/test_ruian.py |
| utils/parsing/ruian.py | sjtsk_to_wgs84 | function | tests/test_ruian.py, utils/parsing/geonames.py |
| utils/parsing/ruian.py | wgs84_to_sjtsk | function | utils/parsing/vanished.py |
| utils/parsing/ruian.py | read_vfr | function | (internal use only) |
| utils/parsing/ruian.py | wkb_multipolygon | function | (internal use only) |
| utils/parsing/ruian.py | write_geoparquet | function | tests/test_ruian.py |
| utils/parsing/ruian.py | read_geoparquet_polygons | function | tests/test_ruian.py |
| utils/parsing/ruian.py | build_tables | function | utils/reference_tools/places/run_amateur_theatre_pilot.py, utils/scraping/scraper_amaterskedivadlo.py |
| utils/parsing/ruian.py | obce_adjacency | function | tests/test_ruian.py |
| utils/parsing/ruian.py | write_tables | function | (internal use only) |
| utils/parsing/ruian.py | ruian_report | function | (internal use only) |
| utils/parsing/ruian.py | locate_courts | function | utils/parsing/prng.py, utils/parsing/vanished.py |
| utils/parsing/ruian.py | locate_obce | function | utils/parsing/vanished.py |
| utils/parsing/ruian.py | distance_to_okres | function | (internal use only) |
| utils/parsing/ruian.py | name_key | function | tests/test_ruian.py |
| utils/parsing/ruian.py | modern_okres_key | function | tests/test_ruian.py |
| utils/parsing/ruian.py | candidate_places | function | (internal use only) |
| utils/parsing/ruian.py | half_key | function | tests/test_ruian.py |
| utils/parsing/ruian.py | prefix_key | function | tests/test_ruian.py |
| utils/parsing/ruian.py | register_courts | function | tests/test_ruian.py |
| utils/parsing/ruian.py | build_crosswalk | function | tests/test_ruian.py |
| utils/parsing/ruian.py | coord_refs | function | (internal use only) |
| utils/parsing/ruian.py | crosswalk_report | function | utils/lexikon_1910/lexikon_1910_crosswalk.py, utils/lexikon_1910/lexikon_1910_register.py |
| utils/parsing/ruian.py | build_current_geography | function | 02_data_cleaning.ipynb |
| utils/parsing/schema.py | to_english | function | 02_data_cleaning.ipynb |
| utils/parsing/schema.py | schema_report | function | 02_data_cleaning.ipynb |
| utils/parsing/schema.py | write_data_dictionary | function | 02_data_cleaning.ipynb |
| utils/parsing/spatial.py | laea_forward | function | tests/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.py | read_polygons | function | utils/docs/build_docs.py, utils/reference_tools/places/map_matice.py |
| utils/parsing/spatial.py | CourtLocator | class | 02_data_cleaning.ipynb |
| utils/parsing/spatial.py | add_court_ids | function | (internal use only) |
| utils/parsing/spatial.py | add_settlement_courts | function | 02_data_cleaning.ipynb |
| utils/parsing/spatial.py | settlement_court_report | function | 02_data_cleaning.ipynb |
| utils/parsing/theater.py | extract_theater | function | (internal use only) |
| utils/parsing/theater.py | theater_selftest | function | utils/validation/data_checks.py |
| utils/parsing/theater.py | add_theater_column | function | 02_data_cleaning.ipynb |
| utils/parsing/theater.py | theater_report | function | 02_data_cleaning.ipynb |
| utils/parsing/units.py | extract_regiment | function | — no call site found — |
| utils/parsing/units.py | extract_army | function | — no call site found — |
| utils/parsing/units.py | make_unit_key | function | (internal use only) |
| utils/parsing/units.py | extract_unit_type | function | (internal use only) |
| utils/parsing/units.py | translate_unit | function | — no call site found — |
| utils/parsing/units.py | add_unit_columns | function | 02_data_cleaning.ipynb |
| utils/parsing/units.py | unit_report | function | 02_data_cleaning.ipynb |
| utils/parsing/vanished.py | fetch_vanished | function | utils/parsing/ruian.py |
| utils/parsing/vanished.py | build_vanished | function | utils/parsing/ruian.py |
| utils/parsing/vanished.py | vanished_report | function | utils/parsing/ruian.py |
| utils/parsing/vua_courts.py | read_inventory_lines | function | (internal use only) |
| utils/parsing/vua_courts.py | split_entries | function | utils/lexikon_1910/check_transcription.py |
| utils/parsing/vua_courts.py | parse_paragraphs | function | tests/test_vua_courts.py |
| utils/parsing/vua_courts.py | parse_entry | function | tests/test_unit_timeline.py, utils/reference_tools/units/build_unit_timeline.py |
| utils/parsing/vua_courts.py | parse_attributes | function | tests/test_vua_courts.py |
| utils/parsing/vua_courts.py | parse_name | function | tests/test_vua_courts.py |
| utils/parsing/vua_courts.py | split_accused | function | tests/test_vua_courts.py |
| utils/parsing/vua_courts.py | parse_lines | function | tests/test_vua_courts.py |
| utils/parsing/vua_courts.py | parse_inventory | function | (internal use only) |
| utils/parsing/vua_courts.py | parse_all | function | utils/parsing/matice.py, utils/reference_tools/places/run_matice_pilot.py, utils/reference_tools/units/run_vua_courts_pilot.py |
| utils/parsing/vua_courts.py | courts_report | function | tests/test_vua_courts.py, utils/reference_tools/units/run_vua_courts_pilot.py |
| utils/parsing/vua_courts.py | habsburg_unit_key | function | (internal use only) |
| utils/parsing/vua_courts.py | pilot_link | function | tests/test_vua_courts.py, utils/reference_tools/units/run_vua_courts_pilot.py |
| utils/parsing/vua_courts.py | pilot_link_report | function | tests/test_vua_courts.py, utils/reference_tools/units/run_vua_courts_pilot.py |
| utils/parsing/wikidata.py | fetch_cz_settlements | function | 02_data_cleaning.ipynb |
| utils/parsing/wikidata.py | add_settlement_coords | function | 02_data_cleaning.ipynb |
| utils/parsing/wikidata.py | add_settlement_de_name | function | 02_data_cleaning.ipynb |
| utils/parsing/wikidata.py | add_birth_coords | function | — no call site found — |
| utils/parsing/wikidata.py | okres_centroids | function | 02_data_cleaning.ipynb |
| utils/parsing/wikidata.py | wikidata_report | function | 02_data_cleaning.ipynb |
| utils/plotting/th_style_v113.py | get_palette | function | 03_visualisation.ipynb |
| utils/plotting/th_style_v113.py | set_yaxis_thousands | function | 03_visualisation.ipynb |
| utils/plotting/th_style_v113.py | set_xaxis_thousands | function | 03_visualisation.ipynb |
| utils/plotting/th_style_v113.py | annotate_bars | function | — no call site found — |
| utils/plotting/th_style_v113.py | despine | function | — no call site found — |
| utils/plotting/th_style_v113.py | add_vspans | function | — no call site found — |
| utils/plotting/th_style_v113.py | add_vlines | function | 03_visualisation.ipynb |
| utils/plotting/th_style_v113.py | style_legend | function | 03_visualisation.ipynb |
| utils/plotting/th_style_v113.py | set_academic_style | function | 03_visualisation.ipynb, utils/reference_tools/places/map_intensities.py, utils/reference_tools/places/map_matice.py |
| utils/plotting/th_style_v113.py | save_for_latex | function | 03_visualisation.ipynb, utils/reference_tools/places/map_intensities.py, utils/reference_tools/places/map_matice.py |
| utils/reference_tools/background/prep_slices.py | epub_pages | function | (internal use only) |
| utils/reference_tools/background/prep_slices.py | pdf_pages | function | (internal use only) |
| utils/reference_tools/background/prep_slices.py | diary_pages | function | (internal use only) |
| utils/reference_tools/background/prep_slices.py | write_slices | function | (internal use only) |
| utils/reference_tools/background/verify_locators.py | norm | function | tests/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.py | pages | function | 15_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.py | rows | function | tests/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.py | main | function | (internal use only) |
| utils/reference_tools/education/check_unterricht_transcription.py | Result | class | (internal use only) |
| utils/reference_tools/education/check_unterricht_transcription.py | check_table | function | (internal use only) |
| utils/reference_tools/education/check_unterricht_transcription.py | main | function | (internal use only) |
| utils/reference_tools/education/draw_education_sample.py | classify | function | tests/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.py | systematic | function | (internal use only) |
| utils/reference_tools/education/draw_education_sample.py | main | function | (internal use only) |
| utils/reference_tools/education/run_unterricht_check.py | run_taxonomy | function | (internal use only) |
| utils/reference_tools/education/run_unterricht_check.py | run_continuation | function | (internal use only) |
| utils/reference_tools/education/run_unterricht_check.py | run_klassen | function | (internal use only) |
| utils/reference_tools/education/run_unterricht_check.py | run_composition | function | utils/docs/build_docs.py, utils/reference_tools/occupations/run_census_berufsstellung_check.py |
| utils/reference_tools/education/run_unterricht_check.py | run_language | function | (internal use only) |
| utils/reference_tools/education/run_unterricht_check.py | main | function | (internal use only) |
| utils/reference_tools/education/score_education_gold.py | human_rung | function | (internal use only) |
| utils/reference_tools/education/score_education_gold.py | main | function | (internal use only) |
| utils/reference_tools/occupations/build_conflict_workbook.py | candidates | function | utils/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.py | build_conflicts | function | (internal use only) |
| utils/reference_tools/occupations/build_conflict_workbook.py | build_czisco_only | function | (internal use only) |
| utils/reference_tools/occupations/build_conflict_workbook.py | build_decisions | function | (internal use only) |
| utils/reference_tools/occupations/build_conflict_workbook.py | verify_xlsx | function | (internal use only) |
| utils/reference_tools/occupations/build_conflict_workbook.py | load_curation | function | (internal use only) |
| utils/reference_tools/occupations/build_conflict_workbook.py | main | function | (internal use only) |
| utils/reference_tools/occupations/build_czisco_bridge.py | head_nouns | function | — no call site found — |
| utils/reference_tools/occupations/build_czisco_bridge.py | matches_head | function | utils/reference_tools/occupations/run_czisco_check.py |
| utils/reference_tools/occupations/build_czisco_bridge.py | build | function | (internal use only) |
| utils/reference_tools/occupations/build_czisco_bridge.py | bridge_report | function | utils/reference_tools/occupations/run_link_bridge.py |
| utils/reference_tools/occupations/build_czisco_bridge.py | main | function | (internal use only) |
| utils/reference_tools/occupations/build_hisco_authority.py | load_hierarchy | function | (internal use only) |
| utils/reference_tools/occupations/build_hisco_authority.py | load_code_lists | function | (internal use only) |
| utils/reference_tools/occupations/build_hisco_authority.py | load_hsn_scales | function | (internal use only) |
| utils/reference_tools/occupations/build_hisco_authority.py | build_authority | function | utils/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.py | authority_report | function | (internal use only) |
| utils/reference_tools/occupations/build_hisco_authority.py | main | function | (internal use only) |
| utils/reference_tools/occupations/build_occupation_curation.py | key_frequencies | function | (internal use only) |
| utils/reference_tools/occupations/build_occupation_curation.py | build | function | (internal use only) |
| utils/reference_tools/occupations/build_occupation_curation.py | curation_report | function | (internal use only) |
| utils/reference_tools/occupations/build_occupation_curation.py | main | function | (internal use only) |
| utils/reference_tools/occupations/czech_glosses.py | duplicate_keys | function | utils/reference_tools/occupations/run_link_bridge.py |
| utils/reference_tools/occupations/download_hisco_sources.py | sha256 | function | utils/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.py | fetch | function | tests/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.py | download_druid | function | (internal use only) |
| utils/reference_tools/occupations/download_hisco_sources.py | check_hsn | function | (internal use only) |
| utils/reference_tools/occupations/download_hisco_sources.py | write_manifest | function | utils/scraping/run_matice_downloads.py |
| utils/reference_tools/occupations/download_hisco_sources.py | main | function | (internal use only) |
| utils/reference_tools/occupations/run_census_berufsstellung_check.py | run | function | (internal use only) |
| utils/reference_tools/occupations/run_census_berufsstellung_check.py | run_composition | function | utils/docs/build_docs.py, utils/reference_tools/education/run_unterricht_check.py |
| utils/reference_tools/occupations/run_czisco_check.py | load_bridge | function | (internal use only) |
| utils/reference_tools/occupations/run_czisco_check.py | best_match | function | (internal use only) |
| utils/reference_tools/occupations/run_czisco_check.py | check | function | 15_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.py | tail_candidates | function | (internal use only) |
| utils/reference_tools/occupations/run_czisco_check.py | main | function | (internal use only) |
| utils/reference_tools/occupations/run_link_bridge.py | load_authority_codes | function | (internal use only) |
| utils/reference_tools/occupations/run_link_bridge.py | load_german_index | function | (internal use only) |
| utils/reference_tools/occupations/run_link_bridge.py | load_label_index | function | (internal use only) |
| utils/reference_tools/occupations/run_link_bridge.py | bridge | function | utils/docs/build_docs.py |
| utils/reference_tools/occupations/run_link_bridge.py | apply_adjudication | function | (internal use only) |
| utils/reference_tools/occupations/run_link_bridge.py | bridge_report | function | utils/reference_tools/occupations/build_czisco_bridge.py |
| utils/reference_tools/occupations/run_link_bridge.py | main | function | (internal use only) |
| utils/reference_tools/occupations/run_mosaic_beruf_check.py | run_vocabulary | function | (internal use only) |
| utils/reference_tools/occupations/run_mosaic_beruf_check.py | run_position | function | (internal use only) |
| utils/reference_tools/occupations/run_mosaic_beruf_check.py | run_age_band | function | (internal use only) |
| utils/reference_tools/occupations/run_mosaic_beruf_check.py | run_keys | function | (internal use only) |
| utils/reference_tools/occupations/run_mosaic_beruf_check.py | run_stability | function | (internal use only) |
| utils/reference_tools/occupations/run_occcanine_check.py | agreement | function | utils/docs/build_docs.py |
| utils/reference_tools/occupations/run_occcanine_check.py | load_model | function | (internal use only) |
| utils/reference_tools/occupations/run_occcanine_check.py | check | function | 15_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.py | main | function | (internal use only) |
| utils/reference_tools/parties/check_atlas_parties.py | load | function | utils/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.py | run_checks | function | utils/docs/build_docs.py, utils/validation/data_checks.py |
| utils/reference_tools/parties/check_atlas_parties.py | main | function | (internal use only) |
| utils/reference_tools/places/audit_full_run_pages.py | audit_page | function | — no call site found — |
| utils/reference_tools/places/audit_full_run_pages.py | flags | function | 15_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.py | main | function | (internal use only) |
| utils/reference_tools/places/build_curation.py | main | function | (internal use only) |
| utils/reference_tools/places/build_german_click_queue.py | entries_of | function | (internal use only) |
| utils/reference_tools/places/build_german_click_queue.py | build | function | (internal use only) |
| utils/reference_tools/places/build_german_click_queue.py | main | function | (internal use only) |
| utils/reference_tools/places/build_german_click_strips.py | page_tokens | function | (internal use only) |
| utils/reference_tools/places/build_german_click_strips.py | main | function | (internal use only) |
| utils/reference_tools/places/build_matice_read_sheets.py | alto_boxes | function | (internal use only) |
| utils/reference_tools/places/build_matice_read_sheets.py | crop_entry | function | (internal use only) |
| utils/reference_tools/places/build_matice_read_sheets.py | label | function | 02_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.py | build_sheets | function | (internal use only) |
| utils/reference_tools/places/build_matice_read_sheets.py | main | function | (internal use only) |
| utils/reference_tools/places/build_sk_curation.py | main | function | (internal use only) |
| utils/reference_tools/places/build_theatre_gold_page.py | page_clauses | function | (internal use only) |
| utils/reference_tools/places/build_theatre_gold_page.py | build | function | (internal use only) |
| utils/reference_tools/places/german_candidate_diagnosis.py | filter_of | function | (internal use only) |
| utils/reference_tools/places/german_candidate_diagnosis.py | main | function | (internal use only) |
| utils/reference_tools/places/german_click_recall.py | load_clicks | function | utils/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.py | inside | function | 16_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.py | candidate_recall | function | utils/reference_tools/places/german_supervised_placement.py |
| utils/reference_tools/places/german_click_recall.py | placement_accuracy | function | utils/reference_tools/places/german_supervised_placement.py |
| utils/reference_tools/places/german_click_recall.py | report | function | 02_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.py | main | function | (internal use only) |
| utils/reference_tools/places/german_locator_trial.py | german_sections | function | 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/german_locator_trial.py | safe_plate_templates | function | utils/reference_tools/places/german_click_recall.py |
| utils/reference_tools/places/german_locator_trial.py | placed_share | function | (internal use only) |
| utils/reference_tools/places/german_locator_trial.py | main | function | (internal use only) |
| utils/reference_tools/places/german_supervised_placement.py | label_candidates | function | (internal use only) |
| utils/reference_tools/places/german_supervised_placement.py | place_all | function | (internal use only) |
| utils/reference_tools/places/german_supervised_placement.py | main | function | (internal use only) |
| utils/reference_tools/places/map_intensities.py | Basemap | class | — no call site found — |
| utils/reference_tools/places/map_intensities.py | court_values | function | utils/reference_tools/places/map_matice.py |
| utils/reference_tools/places/map_intensities.py | district_figure | function | (internal use only) |
| utils/reference_tools/places/map_intensities.py | municipal_figure | function | (internal use only) |
| utils/reference_tools/places/map_intensities.py | main | function | (internal use only) |
| utils/reference_tools/places/map_matice.py | court_shapes | function | utils/reference_tools/places/map_intensities.py |
| utils/reference_tools/places/map_matice.py | border_segments | function | utils/reference_tools/places/map_intensities.py |
| utils/reference_tools/places/map_matice.py | base_map | function | (internal use only) |
| utils/reference_tools/places/map_matice.py | okres_values | function | (internal use only) |
| utils/reference_tools/places/map_matice.py | court_values | function | utils/reference_tools/places/map_intensities.py |
| utils/reference_tools/places/map_matice.py | settlement_counts | function | utils/docs/build_docs.py |
| utils/reference_tools/places/map_matice.py | municipal_map | function | (internal use only) |
| utils/reference_tools/places/map_matice.py | main | function | (internal use only) |
| utils/reference_tools/places/merge_matice_readings.py | main | function | (internal use only) |
| utils/reference_tools/places/run_amateur_theatre_pilot.py | run | function | (internal use only) |
| utils/reference_tools/places/run_amateur_theatre_pilot.py | main | function | (internal use only) |
| utils/reference_tools/places/run_german_detect_by_naming.py | candidate_glyphs | function | (internal use only) |
| utils/reference_tools/places/run_german_detect_by_naming.py | threshold_for | function | (internal use only) |
| utils/reference_tools/places/run_german_detect_by_naming.py | score_page | function | (internal use only) |
| utils/reference_tools/places/run_german_detect_by_naming.py | main | function | (internal use only) |
| utils/reference_tools/places/run_german_oracle.py | read_clicks | function | utils/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.py | click_glyphs | function | utils/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.py | interval | function | (internal use only) |
| utils/reference_tools/places/run_german_oracle.py | measure | function | tests/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.py | per_token | function | (internal use only) |
| utils/reference_tools/places/run_german_oracle.py | leave_one_page_out | function | (internal use only) |
| utils/reference_tools/places/run_german_oracle.py | main | function | (internal use only) |
| utils/reference_tools/places/run_german_reader_boxes.py | section_images | function | (internal use only) |
| utils/reference_tools/places/run_german_reader_boxes.py | clicked_pages | function | (internal use only) |
| utils/reference_tools/places/run_german_reader_boxes.py | read | function | 02_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.py | reader_marks | function | (internal use only) |
| utils/reference_tools/places/run_german_reader_boxes.py | classify_marks | function | (internal use only) |
| utils/reference_tools/places/run_german_reader_boxes.py | box_report | function | (internal use only) |
| utils/reference_tools/places/run_german_reader_boxes.py | snap_boxes | function | (internal use only) |
| utils/reference_tools/places/run_german_reader_boxes.py | negative_glyphs | function | (internal use only) |
| utils/reference_tools/places/run_german_reader_boxes.py | judge | function | utils/reference_tools/places/statutory_institutions.py, utils/scraping/run_matice_downloads.py |
| utils/reference_tools/places/run_german_reader_boxes.py | main | function | (internal use only) |
| utils/reference_tools/places/run_intensities.py | load_legion | function | (internal use only) |
| utils/reference_tools/places/run_intensities.py | theatre_mean | function | (internal use only) |
| utils/reference_tools/places/run_intensities.py | add_rates | function | (internal use only) |
| utils/reference_tools/places/run_intensities.py | own_matice | function | (internal use only) |
| utils/reference_tools/places/run_intensities.py | okres_table | function | (internal use only) |
| utils/reference_tools/places/run_intensities.py | court_okres | function | (internal use only) |
| utils/reference_tools/places/run_intensities.py | court_table | function | (internal use only) |
| utils/reference_tools/places/run_intensities.py | legion_settlements | function | (internal use only) |
| utils/reference_tools/places/run_intensities.py | settlement_table | function | (internal use only) |
| utils/reference_tools/places/run_intensities.py | main | function | (internal use only) |
| utils/reference_tools/places/run_matice_pilot.py | place_index | function | utils/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.py | add_places | function | (internal use only) |
| utils/reference_tools/places/run_matice_pilot.py | apply_readings | function | tests/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.py | add_occupations | function | (internal use only) |
| utils/reference_tools/places/run_matice_pilot.py | report | function | 02_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.py | theatre_rate | function | (internal use only) |
| utils/reference_tools/places/run_matice_pilot.py | theatre_check | function | utils/docs/build_docs.py |
| utils/reference_tools/places/run_matice_pilot.py | main | function | (internal use only) |
| utils/reference_tools/places/run_matice_pilot.py | payment_report | function | (internal use only) |
| utils/reference_tools/places/run_matice_pilot.py | register_check | function | utils/validation/check_ostafin_silesia.py |
| utils/reference_tools/places/run_matice_pilot.py | court_level | function | (internal use only) |
| utils/reference_tools/places/score_sealed_abbreviations.py | upper_bound | function | tests/test_sealed_abbreviations.py |
| utils/reference_tools/places/score_sealed_abbreviations.py | parse_language_counts | function | tests/test_sealed_abbreviations.py |
| utils/reference_tools/places/score_sealed_abbreviations.py | route_side | function | (internal use only) |
| utils/reference_tools/places/score_sealed_abbreviations.py | gold_side | function | (internal use only) |
| utils/reference_tools/places/score_sealed_abbreviations.py | sides | function | tests/test_sealed_scorer.py, utils/lexikon_1910/score_certification.py, utils/validation/score_sealed_retest.py |
| utils/reference_tools/places/score_sealed_abbreviations.py | merge | function | 02_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.py | block | function | tests/test_de_table.py |
| utils/reference_tools/places/score_sealed_abbreviations.py | measure | function | tests/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.py | report | function | 02_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.py | write | function | tests/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.py | main | function | (internal use only) |
| utils/reference_tools/places/statutory_institutions.py | vocabulary | function | tests/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.py | edition_text | function | (internal use only) |
| utils/reference_tools/places/statutory_institutions.py | prompt | function | (internal use only) |
| utils/reference_tools/places/statutory_institutions.py | schema | function | 02_data_cleaning.ipynb |
| utils/reference_tools/places/statutory_institutions.py | read | function | 02_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.py | check | function | 15_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.py | canonical_code | function | tests/test_statutory_institutions.py |
| utils/reference_tools/places/statutory_institutions.py | expansion_codes | function | tests/test_statutory_institutions.py |
| utils/reference_tools/places/statutory_institutions.py | span_groups | function | tests/test_statutory_institutions.py |
| utils/reference_tools/places/statutory_institutions.py | compare | function | tests/test_statutory_institutions.py, utils/lexikon_1910/score_r5_german_notes.py |
| utils/reference_tools/places/statutory_institutions.py | judge_prompt | function | (internal use only) |
| utils/reference_tools/places/statutory_institutions.py | judge_schema | function | (internal use only) |
| utils/reference_tools/places/statutory_institutions.py | judge | function | utils/reference_tools/places/run_german_reader_boxes.py, utils/scraping/run_matice_downloads.py |
| utils/reference_tools/places/statutory_institutions.py | adjudicated | function | tests/test_statutory_institutions.py |
| utils/reference_tools/places/statutory_institutions.py | load_rulings | function | tests/test_statutory_institutions.py, utils/reference_tools/places/statutory_prose.py |
| utils/reference_tools/places/statutory_institutions.py | apply_rulings | function | tests/test_statutory_institutions.py |
| utils/reference_tools/places/statutory_institutions.py | mark_totals | function | tests/test_statutory_institutions.py |
| utils/reference_tools/places/statutory_institutions.py | by_category | function | tests/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.py | main | function | (internal use only) |
| utils/reference_tools/places/statutory_prose.py | render | function | utils/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.py | text_layer | function | (internal use only) |
| utils/reference_tools/places/statutory_prose.py | ink_mask | function | utils/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.py | line_bands | function | (internal use only) |
| utils/reference_tools/places/statutory_prose.py | print_columns | function | (internal use only) |
| utils/reference_tools/places/statutory_prose.py | strips | function | utils/reference_tools/places/build_german_click_strips.py |
| utils/reference_tools/places/statutory_prose.py | read_strip | function | (internal use only) |
| utils/reference_tools/places/statutory_prose.py | estimate | function | tests/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.py | clean_reply | function | tests/test_statutory_prose.py |
| utils/reference_tools/places/statutory_prose.py | normalise | function | tests/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.py | tokens | function | tests/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.py | load_rulings | function | tests/test_statutory_institutions.py, utils/reference_tools/places/statutory_institutions.py |
| utils/reference_tools/places/statutory_prose.py | align | function | tests/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.py | disagreement_kind | function | (internal use only) |
| utils/reference_tools/places/statutory_prose.py | detokenise | function | tests/test_statutory_prose.py |
| utils/reference_tools/places/statutory_prose.py | main | function | (internal use only) |
| utils/reference_tools/units/build_unit_authority.py | read_entries | function | utils/reference_tools/units/build_unit_timeline.py |
| utils/reference_tools/units/build_unit_authority.py | headword_number | function | — no call site found — |
| utils/reference_tools/units/build_unit_authority.py | build | function | (internal use only) |
| utils/reference_tools/units/build_unit_authority.py | main | function | (internal use only) |
| utils/reference_tools/units/build_unit_timeline.py | sentences | function | tests/test_unit_timeline.py |
| utils/reference_tools/units/build_unit_timeline.py | split_phases | function | tests/test_unit_timeline.py |
| utils/reference_tools/units/build_unit_timeline.py | parse_states | function | tests/test_unit_timeline.py |
| utils/reference_tools/units/build_unit_timeline.py | parse_period | function | (internal use only) |
| utils/reference_tools/units/build_unit_timeline.py | parse_commanders | function | (internal use only) |
| utils/reference_tools/units/build_unit_timeline.py | parse_parents | function | tests/test_unit_timeline.py |
| utils/reference_tools/units/build_unit_timeline.py | parse_lifecycle | function | (internal use only) |
| utils/reference_tools/units/build_unit_timeline.py | parse_transport | function | (internal use only) |
| utils/reference_tools/units/build_unit_timeline.py | parse_entry | function | tests/test_unit_timeline.py, utils/parsing/vua_courts.py |
| utils/reference_tools/units/build_unit_timeline.py | out_of_order | function | tests/test_lexikon_1910_order.py, tests/test_unit_timeline.py, utils/lexikon_1910/lexikon_1910_institutions.py |
| utils/reference_tools/units/build_unit_timeline.py | flag_suspect | function | (internal use only) |
| utils/reference_tools/units/build_unit_timeline.py | build | function | (internal use only) |
| utils/reference_tools/units/build_unit_timeline.py | main | function | (internal use only) |
| utils/reference_tools/units/check_unit_timeline.py | load | function | utils/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.py | checks | function | (internal use only) |
| utils/reference_tools/units/check_unit_timeline.py | main | function | (internal use only) |
| utils/reference_tools/units/run_vua_courts_pilot.py | run | function | (internal use only) |
| utils/scraping/fetch_vua_courts.py | write_source_note | function | utils/scraping/run_matice_downloads.py, utils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_verlustlisten.py |
| utils/scraping/fetch_vua_courts.py | fetch_inventories | function | (internal use only) |
| utils/scraping/progress.py | has_tqdm | function | — no call site found — |
| utils/scraping/progress.py | page_bar | function | utils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_kramerius.py, utils/scraping/scraper_kramerius7.py, utils/scraping/scraper_verlustlisten.py |
| utils/scraping/progress.py | redirect_logging | function | utils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_kramerius.py, utils/scraping/scraper_kramerius7.py, utils/scraping/scraper_verlustlisten.py |
| utils/scraping/run_lexicon_downloads.py | run_job | function | utils/scraping/run_matice_downloads.py |
| utils/scraping/run_lexicon_downloads.py | summarise | function | utils/lexikon_1910/locate_glyphs.py, utils/scraping/run_oeulk_downloads.py |
| utils/scraping/run_lexicon_downloads.py | main | function | (internal use only) |
| utils/scraping/run_matice_downloads.py | payment_jobs | function | (internal use only) |
| utils/scraping/run_matice_downloads.py | all_jobs | function | (internal use only) |
| utils/scraping/run_matice_downloads.py | robots_disallowed | function | (internal use only) |
| utils/scraping/run_matice_downloads.py | resolve_slice | function | (internal use only) |
| utils/scraping/run_matice_downloads.py | check_positions | function | (internal use only) |
| utils/scraping/run_matice_downloads.py | solr | function | (internal use only) |
| utils/scraping/run_matice_downloads.py | payment_entries | function | (internal use only) |
| utils/scraping/run_matice_downloads.py | list_runs | function | tests/test_matice.py |
| utils/scraping/run_matice_downloads.py | issue_label | function | tests/test_matice.py |
| utils/scraping/run_matice_downloads.py | discover_payments | function | (internal use only) |
| utils/scraping/run_matice_downloads.py | discover_issue | function | (internal use only) |
| utils/scraping/run_matice_downloads.py | write_manifest | function | utils/reference_tools/occupations/download_hisco_sources.py |
| utils/scraping/run_matice_downloads.py | assemble_slice | function | (internal use only) |
| utils/scraping/run_matice_downloads.py | run_job | function | utils/scraping/run_lexicon_downloads.py |
| utils/scraping/run_matice_downloads.py | write_source_note | function | utils/scraping/fetch_vua_courts.py, utils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_verlustlisten.py |
| utils/scraping/run_matice_downloads.py | dry_run | function | utils/lexikon_1910/run_r5_german_notes.py, utils/lexikon_1910/run_verify_test_b.py |
| utils/scraping/run_matice_downloads.py | main | function | (internal use only) |
| utils/scraping/run_oeulk_downloads.py | volume_key | function | tests/test_scraper_kramerius.py |
| utils/scraping/run_oeulk_downloads.py | list_volumes | function | (internal use only) |
| utils/scraping/run_oeulk_downloads.py | pdf_page_count | function | (internal use only) |
| utils/scraping/run_oeulk_downloads.py | pages_on_disk | function | (internal use only) |
| utils/scraping/run_oeulk_downloads.py | run_volume | function | (internal use only) |
| utils/scraping/run_oeulk_downloads.py | summarise | function | utils/lexikon_1910/locate_glyphs.py, utils/scraping/run_lexicon_downloads.py |
| utils/scraping/run_oeulk_downloads.py | main | function | (internal use only) |
| utils/scraping/scraper_amaterskedivadlo.py | HistoryViewError | class | (internal use only) |
| utils/scraping/scraper_amaterskedivadlo.py | BlockedError | class | utils/scraping/scraper_verlustlisten.py |
| utils/scraping/scraper_amaterskedivadlo.py | ServerUnhealthyError | class | utils/scraping/scraper_verlustlisten.py |
| utils/scraping/scraper_amaterskedivadlo.py | RequestBudgetExceeded | class | utils/scraping/scraper_verlustlisten.py |
| utils/scraping/scraper_amaterskedivadlo.py | parse_map_xml | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | parse_year_token | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | parse_span | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | parse_ensemble_list | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | parse_first_record | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | parse_place_page | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | parse_place_ensembles | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | parse_activity_decades | function | tests/test_amateur_theatre.py |
| utils/scraping/scraper_amaterskedivadlo.py | parse_ensemble_page | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | view_state | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | is_history_view | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | TheatreClient | class | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | write_source_note | function | utils/scraping/fetch_vua_courts.py, utils/scraping/run_matice_downloads.py, utils/scraping/scraper_verlustlisten.py |
| utils/scraping/scraper_amaterskedivadlo.py | fetch_places | function | (internal use only) |
| utils/scraping/scraper_amaterskedivadlo.py | places_table | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | select_geo_ids | function | (internal use only) |
| utils/scraping/scraper_amaterskedivadlo.py | scrape_places | function | (internal use only) |
| utils/scraping/scraper_amaterskedivadlo.py | build_tables | function | utils/parsing/ruian.py, utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/scraping/scraper_amaterskedivadlo.py | compare_sources | function | utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/scraping/scraper_amaterskedivadlo.py | estimate_full_run | function | utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/scraping/scraper_amaterskedivadlo.py | select_ensemble_ids | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | scrape_ensembles | function | (internal use only) |
| utils/scraping/scraper_amaterskedivadlo.py | build_ensemble_pages | function | tests/test_scraper_amaterskedivadlo.py, utils/reference_tools/places/run_amateur_theatre_pilot.py |
| utils/scraping/scraper_amaterskedivadlo.py | parse_abbreviation_page | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | fetch_abbreviations | function | tests/test_scraper_amaterskedivadlo.py |
| utils/scraping/scraper_amaterskedivadlo.py | build_parser | function | utils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_amaterskedivadlo.py | main | function | (internal use only) |
| utils/scraping/scraper_csol.py | ScrapeResult | class | (internal use only) |
| utils/scraping/scraper_csol.py | ScrapeSummary | class | — no call site found — |
| utils/scraping/scraper_csol.py | parse_record | function | tests/test_scraper_verlustlisten.py, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_csol.py | make_session | function | 01_data_collection.ipynb, tests/test_scraper_verlustlisten.py, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_csol.py | fetch_with_retries | function | utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_csol.py | fetch_one | function | (internal use only) |
| utils/scraping/scraper_csol.py | autodetect_max_id | function | 01_data_collection.ipynb, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_csol.py | CSVWriter | class | utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_csol.py | write_xlsx_from_csv | function | (internal use only) |
| utils/scraping/scraper_csol.py | iter_scrape | function | utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_csol.py | scrape_range | function | 01_data_collection.ipynb, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_csol.py | test_id | function | 01_data_collection.ipynb, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_csol.py | validate_output | function | 01_data_collection.ipynb, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_csol.py | build_parser | function | utils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_verlustlisten.py, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_csol.py | main | function | (internal use only) |
| utils/scraping/scraper_kramerius.py | KrameriusClient | class | utils/scraping/run_oeulk_downloads.py |
| utils/scraping/scraper_kramerius.py | parse_pid | function | utils/scraping/scraper_kramerius7.py |
| utils/scraping/scraper_kramerius.py | parse_host | function | utils/scraping/run_matice_downloads.py, utils/scraping/scraper_kramerius7.py |
| utils/scraping/scraper_kramerius.py | safe_filename | function | utils/scraping/scraper_kramerius7.py |
| utils/scraping/scraper_kramerius.py | download_document | function | utils/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.py | main | function | (internal use only) |
| utils/scraping/scraper_kramerius7.py | Kramerius7Client | class | utils/scraping/run_matice_downloads.py |
| utils/scraping/scraper_kramerius7.py | parse_pid | function | utils/scraping/scraper_kramerius.py |
| utils/scraping/scraper_kramerius7.py | parse_host | function | utils/scraping/run_matice_downloads.py, utils/scraping/scraper_kramerius.py |
| utils/scraping/scraper_kramerius7.py | safe_filename | function | utils/scraping/scraper_kramerius.py |
| utils/scraping/scraper_kramerius7.py | download_document | function | utils/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.py | main | function | (internal use only) |
| utils/scraping/scraper_verlustlisten.py | BlockedError | class | utils/scraping/scraper_amaterskedivadlo.py |
| utils/scraping/scraper_verlustlisten.py | ServerUnhealthyError | class | utils/scraping/scraper_amaterskedivadlo.py |
| utils/scraping/scraper_verlustlisten.py | RequestBudgetExceeded | class | utils/scraping/scraper_amaterskedivadlo.py |
| utils/scraping/scraper_verlustlisten.py | CanaryError | class | (internal use only) |
| utils/scraping/scraper_verlustlisten.py | project_of | function | tests/test_scraper_verlustlisten.py |
| utils/scraping/scraper_verlustlisten.py | parse_record | function | tests/test_scraper_verlustlisten.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_verlustlisten.py | make_session | function | 01_data_collection.ipynb, tests/test_scraper_verlustlisten.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_verlustlisten.py | DESClient | class | tests/test_scraper_verlustlisten.py |
| utils/scraping/scraper_verlustlisten.py | read_ledger | function | tests/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.py | write_source_note | function | utils/scraping/fetch_vua_courts.py, utils/scraping/run_matice_downloads.py, utils/scraping/scraper_amaterskedivadlo.py |
| utils/scraping/scraper_verlustlisten.py | walk_ids | function | tests/test_scraper_verlustlisten.py |
| utils/scraping/scraper_verlustlisten.py | check_canary | function | 01_data_collection.ipynb |
| utils/scraping/scraper_verlustlisten.py | scrape_range | function | 01_data_collection.ipynb, utils/scraping/scraper_csol.py, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_verlustlisten.py | pilot_ids | function | tests/test_scraper_verlustlisten.py |
| utils/scraping/scraper_verlustlisten.py | run_pilot | function | 01_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.py | pilot_report | function | 01_data_collection.ipynb |
| utils/scraping/scraper_verlustlisten.py | test_id | function | 01_data_collection.ipynb, utils/scraping/scraper_csol.py, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_verlustlisten.py | build_parser | function | utils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_vuapraha.py |
| utils/scraping/scraper_verlustlisten.py | main | function | (internal use only) |
| utils/scraping/scraper_vuapraha.py | DB | class | (internal use only) |
| utils/scraping/scraper_vuapraha.py | make_session | function | 01_data_collection.ipynb, tests/test_scraper_verlustlisten.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py |
| utils/scraping/scraper_vuapraha.py | fetch_with_retries | function | utils/scraping/scraper_csol.py |
| utils/scraping/scraper_vuapraha.py | parse_record | function | tests/test_scraper_verlustlisten.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py |
| utils/scraping/scraper_vuapraha.py | CSVWriter | class | utils/scraping/scraper_csol.py |
| utils/scraping/scraper_vuapraha.py | autodetect_max_id | function | 01_data_collection.ipynb, utils/scraping/scraper_csol.py |
| utils/scraping/scraper_vuapraha.py | iter_scrape | function | utils/scraping/scraper_csol.py |
| utils/scraping/scraper_vuapraha.py | scrape_range | function | 01_data_collection.ipynb, utils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py |
| utils/scraping/scraper_vuapraha.py | test_id | function | 01_data_collection.ipynb, utils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py |
| utils/scraping/scraper_vuapraha.py | validate_output | function | 01_data_collection.ipynb, utils/scraping/scraper_csol.py |
| utils/scraping/scraper_vuapraha.py | build_parser | function | utils/scraping/scraper_amaterskedivadlo.py, utils/scraping/scraper_csol.py, utils/scraping/scraper_verlustlisten.py |
| utils/scraping/scraper_vuapraha.py | main | function | (internal use only) |
| utils/tables/latex_tables.py | RawLaTeX | class | (internal use only) |
| utils/tables/latex_tables.py | series_table | function | (internal use only) |
| utils/tables/latex_tables.py | summary_table | function | 03_visualisation.ipynb, utils/lexikon_1910/run_e1b_second_reader.py |
| utils/tables/latex_tables.py | regression_table | function | — no call site found — |
| utils/tables/latex_tables.py | exit_code_table | function | 03_visualisation.ipynb |
| utils/tables/latex_tables.py | missing_table | function | 01_data_collection.ipynb |
| utils/tables/latex_tables.py | df_to_latex | function | 03_visualisation.ipynb |
| utils/validation/build_validation_workbook.py | build_place_sheet | function | (internal use only) |
| utils/validation/build_validation_workbook.py | build_rank_sheet | function | (internal use only) |
| utils/validation/build_validation_workbook.py | main | function | (internal use only) |
| utils/validation/check_ostafin_silesia.py | key | function | 02_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.py | german_name | function | tests/test_ostafin_silesia.py |
| utils/validation/check_ostafin_silesia.py | digits | function | tests/test_ostafin_silesia.py |
| utils/validation/check_ostafin_silesia.py | ostafin | function | (internal use only) |
| utils/validation/check_ostafin_silesia.py | communes | function | tests/test_ostafin_silesia.py |
| utils/validation/check_ostafin_silesia.py | census_check | function | (internal use only) |
| utils/validation/check_ostafin_silesia.py | table_rows | function | tests/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.py | join_register | function | tests/test_ostafin_silesia.py |
| utils/validation/check_ostafin_silesia.py | register_check | function | utils/reference_tools/places/run_matice_pilot.py |
| utils/validation/check_ostafin_silesia.py | lexicon_candidates | function | tests/test_ostafin_silesia.py |
| utils/validation/check_ostafin_silesia.py | join_lexicon | function | (internal use only) |
| utils/validation/check_ostafin_silesia.py | cause | function | utils/parsing/amateur_theatre_model.py |
| utils/validation/check_ostafin_silesia.py | lexicon_check | function | (internal use only) |
| utils/validation/check_ostafin_silesia.py | report | function | 02_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.py | main | function | (internal use only) |
| utils/validation/data_checks.py | DataCheckError | class | (internal use only) |
| utils/validation/data_checks.py | Check | class | 02_data_cleaning.ipynb |
| utils/validation/data_checks.py | run_checks | function | utils/docs/build_docs.py, utils/reference_tools/parties/check_atlas_parties.py |
| utils/validation/data_checks.py | place_checks | function | 15_frozen_run.ipynb |
| utils/validation/data_checks.py | report | function | 02_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.py | assert_data_ok | function | 02_data_cleaning.ipynb |
| utils/validation/lexikon_population.py | decode | function | tests/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.py | fold | function | tests/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.py | touches_1910 | function | tests/test_ostafin_silesia.py |
| utils/validation/lexikon_population.py | data_rows | function | (internal use only) |
| utils/validation/lexikon_population.py | note_rows | function | (internal use only) |
| utils/validation/lexikon_population.py | read | function | 02_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.py | queue_label | function | tests/test_run_completeness.py |
| utils/validation/run_completeness.py | missing_replies | function | tests/test_run_completeness.py |
| utils/validation/run_completeness.py | read_gaps | function | 15_frozen_run.ipynb, tests/test_run_completeness.py |
| utils/validation/run_completeness.py | opus_gaps | function | 15_frozen_run.ipynb, tests/test_run_completeness.py |
| utils/validation/run_completeness.py | unaccepted | function | 15_frozen_run.ipynb, tests/test_run_completeness.py |
| utils/validation/run_completeness.py | write_record | function | 15_frozen_run.ipynb, tests/test_run_completeness.py |
| utils/validation/run_completeness.py | report | function | 02_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.py | score_run | function | (internal use only) |
| utils/validation/score_sealed_retest.py | between | function | 03_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.py | report | function | 02_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.py | retest | function | (internal use only) |
| utils/validation/score_sealed_retest.py | main | function | (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
| code | bloc | name_cs | name_en | founded | cechy | morava | slezsko | note_cs | refs |
|---|---|---|---|---|---|---|---|---|---|
| Č1 | czech | Národní strana | National Party (Old Czechs) | 1861 | 1861.0 | 1861.0 | staročeši, 1918 pokračovali v Č 23 | Č23 | |
| Č2 | czech | Národní strana svobodomyslná | National Liberal Party (Young Czechs) | 1874 | 1874.0 | mladočeši, 1918 transformace v Č 23 | Č23 | ||
| Č3 | czech | Lidová strana | People's Party (Moravia) | 1891 | 1891.0 | 1909 po sjednocení s Moravskou str. pokrokovou – Č 17 | Č17 | ||
| Č4 | czech | Křesťanskosociální strana | Christian-Social Party | 1894/98 | 1894.0 | 1899.0 | 1911.0 | v Čechách 1906 nahrazena Č 14 | Č14 |
| Č5 | czech | Katolická strana národní | Catholic National Party | 1896 | 1897.0 | 1896.0 | v Čechách 1906 nahrazena Č 14 | Č14 | |
| Č6 | czech | Strana pokrokových socialistů | Party of Progressive Socialists | 1896 | 1896.0 | zanikla 1897 | |||
| Č7 | czech | Česká strana radikálně pokroková | Czech Radical Progressive Party | 1897 | 1897.0 | 1908 sjednocení s Č 10 v Č 15 | Č10;Č15 | ||
| Č8 | czech | Česká strana národně sociální | Czech National Social Party | 1897 | 1897.0 | 1898.0 | 1898.0 | 1918 po spojení s D7 přejmenována na Č 24 | D7;Č24 |
| Č9 | czech | Agrární strana | Agrarian Party | 1899 | 1899.0 | 1904.0 | 1912.0 | ||
| Č10 | czech | Česká strana státoprávní | Czech State-Right Party | 1899 | 1899.0 | 1908 sjednocení s Č 7 v Č 15 | Č7;Č15 | ||
| Č11 | czech | Křesťansko-sociální strana lidová | Christian-Social People's Party | 1899 | 1899.0 | 1906 nahrazena Č 14, znovu 1912–1913 | Č14 | ||
| Č12 | czech | Česká strana lidová | Czech People's Party (Realists) | 1900 | 1900.0 | realisté, 1905 transformace v Č 13 | Č13 | ||
| Č13 | czech | Česká strana pokroková | Czech Progressive Party | 1905 | 1905.0 | 1907.0 | 1918 spoluzakladatelka Č 23 | Č23 | |
| Č14 | czech | Strana katolického lidu | Party of the Catholic People | 1906 | 1906.0 | 1910 přejmenována na Č 18 | Č18 | ||
| Č15 | czech | Česká strana státoprávně pokroková | Czech State-Right Progressive Party | 1908 | 1908.0 | 1918 spoluzakladatelka Č 23 | Č23 | ||
| Č16 | czech | Strana živnostnictva a obchodnictva českoslovanského | Czecho-Slavonic Tradesmen's and Merchants' Party | 1908 | 1909.0 | 1908.0 | 1909.0 | 1905 první pokusy | |
| Č17 | czech | Lidová strana pokroková | People's Progressive Party (Moravia) | 1909 | 1909.0 | 1918 spoluzakladatelka Č 23 | Č23 | ||
| Č18 | czech | Česká strana křesťansko-sociální | Czech Christian-Social Party | 1910 | 1910.0 | 1916 sloučení v Č 22 | Č22 | ||
| Č19 | czech | Katolicko-národní strana konzervativní | Catholic-National Conservative Party | 1911 | 1911.0 | ||||
| Č20 | czech | Konzervativní strana lidová | Conservative People's Party | 1912 | 1912.0 | zanikla 1914 | |||
| Č21 | czech | Moravská strana lidová | Moravian People's Party | 1913 | 1913.0 | tzv. Bulínova, zanikla 1914 | |||
| Č22 | czech | Spojená strana katolická | United Catholic Party | 1916 | 1916.0 | sjednocení katol. stran v Čechách | |||
| Č23 | czech | Česká státoprávní demokracie | Czech State-Right Democracy | 1918 | 1918.0 | 1918.0 | 1918.0 | 1919 přejmenována na Národní demokracii | |
| Č24 | czech | Česká strana socialistická | Czech Socialist Party | 1918 | 1918.0 | 1918.0 | 1918.0 | pokračovatelka Č 8 | Č8 |
| Č25 | czech | Moravsko-slezská strana lidová | Moravian-Silesian People's Party | 1918 | 1918.0 | 1918.0 | sjednocení kat. stran Moravy a Slezska | ||
| N1 | german | Německá liberální strana | German Liberal Party | 1861 | 1861.0 | 1861.0 | 1861.0 | ústavověrná, 1896 nahrazena N5 | N5 |
| N2 | german | Německá nacionální strana | German National Party | 1881.0 | 1890.0 | 1896 přejmenována na N4 | N4 | ||
| N3 | german | Rakouská křesťansko-sociální strana | Austrian Christian-Social Party | 1891 | 1905.0 | ||||
| N4 | german | Německá lidová strana | German People's Party | 1896 | 1896.0 | pokračovatelka N 2 | N2 | ||
| N5 | german | Německá pokroková strana | German Progressive Party | 1896 | 1896.0 | 1896.0 | 1896.0 | nástupkyně N 1 | N1 |
| N6 | german | Všeněmecká strana | Pan-German Party | 1900 | 1901.0 | 1900.0 | 1901.0 | ||
| N7 | german | Volná všeněmecká strana | Free Pan-German Party | 1903 | 1903.0 | secese z N 6, 1906 přejmenována na N8 | N6;N8 | ||
| N8 | german | Německá radikální strana | German Radical Party | 1906 | 1907.0 | 1906.0 | 1906.0 | nástupkyně N7 | N7 |
| N9 | german | Německá agrární strana | German Agrarian Party | 1905 | 1905.0 | 1914.0 | 1906.0 | ||
| N10 | german | Německá dělnická strana | German Workers' Party | 1904 | 1904.0 | 1906.0 | 1907.0 | 1918 přejmenována na N 11 | N11 |
| N11 | german | Německá nacionálně socialistická dělnická strana | German National Socialist Workers' Party | 1918 | 1918.0 | 1918.0 | 1918.0 | nástupkyně N10 | N10 |
| P1 | polish | Polská národní strana | Polish National Party | 1861 | 1861.0 | honorační | |||
| P2 | polish | Svaz slezských katolíků | Union of Silesian Catholics | 1883 | 1883.0 | ||||
| P3 | polish | Polská lidová společnost | Polish People's Society | 1884 | 1884.0 | evangelická, 1906 přejmenována na P 5 | P5 | ||
| P4 | polish | Radikálně národní strana | Radical National Party | 1897 | 1897.0 | 1906 přejmenována na P 6, odnož haličské PLS | P6 | ||
| P5 | polish | Polská národní strana | Polish National Party | 1906 | 1906.0 | 1911 sloučení v P 7 | P7 | ||
| P6 | polish | Polská lidová strana | Polish People's Party | 1906 | 1906.0 | ||||
| P7 | polish | Polské národní sjednocení | Polish National Union | 1911 | 1911.0 | ||||
| P8 | polish | Slezská lidová strana | Silesian People's Party | 1909 | 1909.0 | šlonzácká, proněmecká | |||
| D1 | workers | Rakouská dělnická strana | Austrian Workers' Party | 1874 | 1874.0 | 1874.0 | 1874.0 | ||
| D2 | workers | Českoslovanská sociálně demokratická strana dělnická | Czecho-Slavonic Social Democratic Workers' Party | 1878 | 1878.0 | 1878.0 | 1878.0 | do 1910/11 autonomní součást celorakouské strany | |
| D3 | workers | Rakouská sociálně demokratická strana dělnická | Austrian Social Democratic Workers' Party | 1888 | 1889.0 | 1889.0 | 1889.0 | obnovena | |
| D4 | workers | Polská sociálně demokratická strana Haliče a Slezska | Polish Social Democratic Party of Galicia and Silesia | 1899 | 1899.0 | ||||
| D5 | workers | Polská sociálně demokratická strana Slezska a Moravy | Polish Social Democratic Party of Silesia and Moravia | 1906 | 1906.0 | 1906.0 | podléhala haličské straně | ||
| D6 | workers | Česká sociálně demokratická dělnická strana | Czech Social Democratic Workers' Party | 1911 | 1911.0 | 1911.0 | 1911.0 | centralistická | |
| D7 | workers | Federace českých anarchokomunistů | Federation of Czech Anarcho-Communists | 1914 | |||||
| V1 | landowner | Strana ústavověrného velkostatku | Party of the Constitutionally Loyal Great Estate | 1861 | 1861.0 | 1861.0 | 1861.0 | ||
| V2 | landowner | Strana konzervativního velkostatku | Party of the Conservative Great Estate | 1861 | 1861.0 | 1861.0 | 1861.0 | ||
| V3 | landowner | Střední strana moravského velkostatku | Centre Party of the Moravian Great Estate | 1864 | 1864.0 | obnovena 1884 | |||
| V4 | landowner | Strana národních velkostatkářů | Party of National Great Landowners | 1894 | 1894.0 | zanikla 1897 |
Unit authorityWhat's in the data
Legion units → theatre, dates, lineage (Fidler 2025). · source: reference/units/unit_authority.csv
361 rows
| unit_cs | number | variant | theatre | kind | formed | disbanded | n_commanders |
|---|---|---|---|---|---|---|---|
| DRUHÝ ČS. LODNÍ TRANSPORT DO FRANCIE | 2.0 | France | transport | 1 | |||
| PRVNÍ ČS. LODNÍ TRANSPORT DO FRANCIE | 1.0 | France | transport | 1 | |||
| TŘETÍ ČS. LODNÍ TRANSPORT DO FRANCIE | 3.0 | France | transport | 1 | |||
| ČTVRTÝ ČS. LODNÍ TRANSPORT DO FRANCIE | 4.0 | France | transport | 1 | |||
| DVACÁTÝ DRUHÝ ČS. STŘELECKÝ PLUK | 22.0 | France | unit | 1918-05-20 | 1 | ||
| DVACÁTÝ PRVNÍ ČS. STŘELECKÝ PLUK | 21.0 | France | unit | 1918-01-19 | 1 | ||
| DVACÁTÝ TŘETÍ ČS. STŘELECKÝ PLUK | 23.0 | France | unit | 1918-12-03 | 1 | ||
| DVACÁTÝ ČTVRTÝ ČS. STŘELECKÝ PRAPOR | 24.0 | France | unit | 1919-01-18 | 1 | ||
| PRVNÍ ČS. STŘELECKÁ BRIGÁDA [II] | 1.0 | II | France | unit | 1918-06-09 | 1 | |
| PÁTÁ ČS. STŘELECKÁ DIVIZE | 5.0 | France | unit | 1918-12-03 | 0 | ||
| VELITELSTVÍ PĚCHOTY 5. ČS. DIVIZE | France | unit | 1918-12-03 | 1 | |||
| VÝCVIKOVÝ TÁBOR ČS. DOBROVOLNÍKŮ | France | unit | 1918-04-08 | 1 | |||
| ČS. NÁHRADNÍ TĚLESO | France | unit | 1918-06-10 | 1 | |||
| ČS. STŘELECKÝ PRAPOR V COGNACU | France | unit | 1917-11-14 | 1 | |||
| ČS. ZVLÁŠTNÍ PRAPOR | France | unit | 1918-11-14 | 1 | |||
| VOJENSKÝ SOUD ČS. SBORU | Italy | institution | 1918-07-15 | 2 | |||
| ZÁLOŽNÍ NEMOCNICE ČS. VOJSKA | Italy | institution | 1918-04-14 | 0 | |||
| ČS. POLNÍ NEMOCNICE | Italy | institution | 1918-11-17 | 0 | |||
| ČS. POLNÍ POŠTA [II] | II | Italy | institution | 1918-11-17 | 0 | ||
| DRUHÁ NÁHRADNÍ ROTA ČS. VOJSKA | 2.0 | Italy | unit | 1918-04-14 | 0 | ||
| DRUHÁ ČS. ZVLÁŠTNÍ BRIGÁDA | 2.0 | Italy | unit | 1918-04-26 | 1 | ||
| DRUHÝ ČS. PRACOVNÍ PRAPOR | 2.0 | Italy | unit | 1918-02-15 | 1 | ||
| DRUHÝ ČS. ZVLÁŠTNÍ PLUK | 2.0 | Italy | unit | 1918-04-22 | 1 | ||
| DVANÁCTÁ ČS. STŘELECKÁ BRIGÁDA | 12.0 | Italy | unit | 1918-07-11 | 1 | ||
| JEDENÁCTÁ ČS. STŘELECKÁ BRIGÁDA | 11.0 | Italy | unit | 1918-07-11 | 1 | ||
| OSMÝ ČS. DOPLŇOVACÍ PRAPOR | 8.0 | Italy | unit | 1918-05-20 | 0 | ||
| OSMÝ ČS. PRACOVNÍ PRAPOR | 8.0 | Italy | unit | 1918-03-15 | 0 | ||
| PRVNÍ NÁHRADNÍ ROTA ČS. VOJSKA | 1.0 | Italy | unit | 1918-04-14 | 0 | ||
| PRVNÍ ČS. PRACOVNÍ PRAPOR [I] | 1.0 | I | Italy | unit | 1918-02-15 | 1 | |
| PRVNÍ ČS. ZVLÁŠTNÍ BRIGÁDA | 1.0 | Italy | unit | 1918-04-26 | 1 | ||
| PRVNÍ ČS. ZVLÁŠTNÍ DIVIZE | 1.0 | Italy | unit | 1918-04-11 | 2 | ||
| PRVNÍ ČS. ZVLÁŠTNÍ PLUK | 1.0 | Italy | unit | 1918-04-25 | 1 | ||
| PÁTÝ ČS. PRACOVNÍ PRAPOR | 5.0 | Italy | unit | 1918-03-01 | 1 | ||
| SEDMÁ ČS. STŘELECKÁ DIVIZE | 7.0 | Italy | unit | 1918-11-17 | 2 | ||
| SEDMÝ ČS. AUTOMOBILNÍ ODDÍL | 7.0 | Italy | unit | 1918-11-17 | 0 | ||
| SEDMÝ ČS. DOPLŇOVACÍ PRAPOR | 7.0 | Italy | unit | 1918-05-20 | 0 | ||
| SEDMÝ ČS. DĚLOSTŘELECKÝ PLUK | 7.0 | Italy | unit | 1918-12-08 | 1 | ||
| SEDMÝ ČS. PRACOVNÍ PRAPOR | 7.0 | Italy | unit | 1918-03-15 | 0 | ||
| SEDMÝ ČS. ZDRAVOTNICKÝ ODDÍL | 7.0 | Italy | unit | 1918-11-17 | 0 | ||
| SEDMÝ ČS. ZÁSOBOVACÍ ODDÍL | 7.0 | Italy | unit | 1918-11-25 | 0 | ||
| SKUPINA KULOMETNÝCH ROT 1. ČS. DIVIZE | Italy | unit | 1918-05-15 | 1 | |||
| SKUPINA KULOMETNÝCH ROT 6. ČS. DIVIZE [II] | II | Italy | unit | 1918-11-17 | 0 | ||
| SKUPINA KULOMETNÝCH ROT 6. ČS. DIVIZE [I] | I | Italy | unit | 1918-07-11 | 1 | ||
| SKUPINA KULOMETNÝCH ROT 7. ČS. DIVIZE | Italy | unit | 1918-11-17 | 1 | |||
| TELEGRAFNÍ ROTA 6. ČS. DIVIZE | Italy | unit | 1918-11-17 | 0 | |||
| TELEGRAFNÍ ROTA 7. ČS. DIVIZE | Italy | unit | 1918-11-17 | 0 | |||
| TELEGRAFNÍ ROTA ČS. SBORU | Italy | unit | 1918-11-17 | 0 | |||
| TŘETÍ ČS. PRACOVNÍ PRAPOR | 3.0 | Italy | unit | 1918-02-15 | 1 | ||
| TŘETÍ ČS. ZVLÁŠTNÍ PLUK | 3.0 | Italy | unit | 1918-04-26 | 1 | ||
| TŘICÁTÝ DEVÁTÝ ČS. STŘELECKÝ PLUK | 39.0 | Italy | unit | 1918-09-25 | 1 | ||
| TŘICÁTÝ DRUHÝ ČS. STŘELECKÝ PLUK | 32.0 | Italy | unit | 1918-06-03 | 1 | ||
| TŘICÁTÝ PRVNÍ ČS. STŘELECKÝ PLUK | 31.0 | Italy | unit | 1918-06-03 | 1 | ||
| TŘICÁTÝ PÁTÝ ČS. STŘELECKÝ PLUK | 35.0 | Italy | unit | 1918-09-13 | 1 | ||
| TŘICÁTÝ TŘETÍ ČS. STŘELECKÝ PLUK | 33.0 | Italy | unit | 1918-06-03 | 1 | ||
| TŘICÁTÝ ČTVRTÝ ČS. STŘELECKÝ PLUK | 34.0 | Italy | unit | 1918-06-03 | 1 | ||
| VELITELSTVÍ DĚLOSTŘELECTVA ČS. SBORU | Italy | unit | 1918-11-17 | 1 | |||
| VELITELSTVÍ PĚCHOTY 6. ČS. DIVIZE | Italy | unit | 1918-11-17 | 1 | |||
| VELITELSTVÍ PĚCHOTY 7. ČS. DIVIZE | Italy | unit | 1918-11-17 | 1 | |||
| VELITELSTVÍ ZDRAVOTNICTVA ČS. SBORU | Italy | unit | 1918-04-16 | 1 | |||
| ČS. ARMÁDNÍ SBOR [II] | II | Italy | unit | 1918-11-17 | 2 | ||
| ČS. NÁRODNÍ STRÁŽ | Italy | unit | 1918-08-09 | 0 | |||
| ČS. SKUPINA JEZDECKÝCH ESKADRON | Italy | unit | 1918-11-17 | 1 | |||
| ČS. TĚŽKÝ DĚLOSTŘELECKÝ ODDÍL | Italy | unit | 1918-12-10 | 1 | |||
| ČS. VOZATAJSKÝ ODDÍL | Italy | unit | 1918-06-03 | 1 | |||
| ČS. ZDRAVOTNICKÝ ODDÍL | Italy | unit | 1918-05-17 | 0 | |||
| ČS. ZVLÁŠTNÍ DOBROVOLNICKÝ SBOR | Italy | unit | 1918-04-11 | 2 | |||
| ČS. ZÁSOBOVACÍ ODDÍL | Italy | unit | 1918-05-21 | 0 | |||
| ČTVRTÝ ČS. PRACOVNÍ PRAPOR | 4.0 | Italy | unit | 1918-02-25 | 1 | ||
| ČTVRTÝ ČS. ZVLÁŠTNÍ PLUK | 4.0 | Italy | unit | 1918-04-23 | 1 | ||
| ŠESTÁ ČS. STŘELECKÁ DIVIZE [II] | 6.0 | II | Italy | unit | 1918-11-17 | 2 | |
| ŠESTÁ ČS. STŘELECKÁ DIVIZE [I] | 6.0 | I | Italy | unit | 1918-07-11 | 2 | |
| ŠESTÝ ČS. AUTOMOBILNÍ ODDÍL | 6.0 | Italy | unit | 1918-11-17 | 0 | ||
| ŠESTÝ ČS. DĚLOSTŘELECKÝ PLUK [II] | 6.0 | II | Italy | unit | 1918-12-10 | 1 | |
| ŠESTÝ ČS. DĚLOSTŘELECKÝ PLUK [I] | 6.0 | I | Italy | unit | 1918-11-12 | 1 | |
| ŠESTÝ ČS. PRACOVNÍ PRAPOR | 6.0 | Italy | unit | 1918-03-01 | 1 | ||
| ŠESTÝ ČS. ZDRAVOTNICKÝ ODDÍL | 6.0 | Italy | unit | 1918-11-17 | 0 | ||
| ŠESTÝ ČS. ZÁSOBOVACÍ ODDÍL | 6.0 | Italy | unit | 1918-11-25 | 0 | ||
| ŽELEZNIČNÍ ROTA ČS. SBORU | Italy | unit | 1918-12-01 | 0 | |||
| ŽENIJNÍ ROTA 6. ČS. DIVIZE | Italy | unit | 1918-11-17 | 0 | |||
| ŽENIJNÍ ROTA 7. ČS. DIVIZE | Italy | unit | 1918-11-17 | 0 | |||
| AUTOMOBILNÍ PARK ČS. VOJSKA | Russia | institution | 1919-08-26 | 1 | |||
| DOČASNÝ POLNÍ SOUD ČS. VOJSKA VE VLADIVOSTOKU | Russia | institution | 1918-09-10 | 2 | |||
| DRUHÁ ČS. VOJENSKÁ NEMOCNICE | 2.0 | Russia | institution | 1919-03-16 | 1 | ||
| DRUHÁ ČS. VOJENSKÁ NEMOCNICE V JEKATĚRINBURGU | 2.0 | Russia | institution | 1918-10-08 | 1 | ||
| DRUHÁ ČS. ZVĚROLÉKAŘSKÁ NEMOCNICE | 2.0 | Russia | institution | 1918-11-05 | 1 | ||
| DRUHÝ POLNÍ SOUD ČS. VOJSKA | 2.0 | Russia | institution | 1918-06-20 | 1 | ||
| DRUHÝ ČS. DIVIZNÍ LAZARET [II] | 2.0 | II | Russia | institution | 1919-02-09 | 1 | |
| DRUHÝ ČS. DIVIZNÍ LAZARET [I] | 2.0 | I | Russia | institution | 1917-11-15 | 1 | |
| DRUHÝ ČS. SANITNÍ VLAK [II] | 2.0 | II | Russia | institution | 1918-05-29 | 1 | |
| DRUHÝ ČS. SANITNÍ VLAK [I] | 2.0 | I | Russia | institution | 1918-02-17 | 1 | |
| DĚLOSTŘELECKÝ SKLAD ČS. SBORU | Russia | institution | 1918-08-05 | 1 | |||
| DĚLOSTŘELECKÝ SKLAD ČS. VOJSKA | Russia | institution | 1919-02-07 | 1 | |||
| EPIDEMIOLOGICKO-BAKTERIOLOGICKÝ VÝZKUMNÝ ÚSTAV ČS. VOJSKA | Russia | institution | 1918-12-20 | 1 | |||
| HLAVNÍ LÉKÁRNICKÝ SKLAD ČS. SBORU | Russia | institution | 1918-08-05 | 1 | |||
| HLAVNÍ LÉKÁRNICKÝ SKLAD ČS. VOJSKA | Russia | institution | 1919-03-04 | 1 | |||
| INTENDANTSTVO 1. ČS. DIVIZE [II] | II | Russia | institution | 1919-02-01 | 1 | ||
| INTENDANTSTVO 1. ČS. DIVIZE [I] | I | Russia | institution | 1917-09-04 | 0 | ||
| INTENDANTSTVO 2. ČS. DIVIZE [II] | II | Russia | institution | 1919-02-01 | 1 | ||
| INTENDANTSTVO 2. ČS. DIVIZE [I] | I | Russia | institution | 1917-10-04 | 0 | ||
| INTENDANTSTVO 3. ČS. DIVIZE | Russia | institution | 1919-02-01 | 1 | |||
| INTENDANTSTVO ČS. BRIGÁDY | Russia | institution | 1917-06-20 | 0 | |||
| INTENDANTSTVO ČS. SBORU | Russia | institution | 1918-04-23 | 1 | |||
| INTENDANTSTVO ČS. VELITELSTVÍ DÁLNÉHO VÝCHODU | Russia | institution | 1919-03-12 | 1 | |||
| KOMISE PRO VÝSTAVBU ČS. VOJENSKÝCH JEDNOTEK | Russia | institution | 1917-04-02 | 1 | |||
| POLNÍ SOUD ČS. VOJSKA V IRKUTSKU | Russia | institution | 1918-07-28 | 1 | |||
| POLNÍ SOUD ČS. VOJSKA V NOVONIKOLAJEVSKU | Russia | institution | 1918-12-03 | 2 | |||
| POLNÍ SOUD ČS. VOJSKA V ČELJABINSKU | Russia | institution | 1918-06-19 | 2 | |||
| PRVNÍ POLNÍ SOUD ČS. VOJSKA | 1.0 | Russia | institution | 1918-06-20 | 1 | ||
| PRVNÍ ČS. DIVIZNÍ LAZARET [II] | 1.0 | II | Russia | institution | 1919-02-09 | 1 | |
| PRVNÍ ČS. DIVIZNÍ LAZARET [I] | 1.0 | I | Russia | institution | 1917-11-15 | 1 | |
| PRVNÍ ČS. SANITNÍ VLAK [II] | 1.0 | II | Russia | institution | 1918-12-05 | 1 | |
| PRVNÍ ČS. SANITNÍ VLAK [I] | 1.0 | I | Russia | institution | 1918-04-07 | 1 | |
| PRVNÍ ČS. VOJENSKÁ NEMOCNICE | 1.0 | Russia | institution | 1919-04-01 | 1 | ||
| PRVNÍ ČS. VOJENSKÁ NEMOCNICE V JEKATĚRINBURGU | 1.0 | Russia | institution | 1918-10-08 | 1 | ||
| PRVNÍ ČS. ZVĚROLÉKAŘSKÁ NEMOCNICE | 1.0 | Russia | institution | 1918-11-05 | 1 | ||
| PÁTÁ ČS. VOJENSKÁ NEMOCNICE | 5.0 | Russia | institution | 1919-06-29 | 1 | ||
| PÁTÝ ČS. NEMOCNIČNÍ VLAK | 5.0 | Russia | institution | 1919-04-26 | 1 | ||
| PÁTÝ ČS. SANITNÍ VLAK | 5.0 | Russia | institution | 1918-07-05 | 1 | ||
| SBOROVÝ SOUD ČS. VOJSKA | Russia | institution | 1919-01-10 | 2 | |||
| SEDMÁ ČS. VOJENSKÁ NEMOCNICE | 7.0 | Russia | institution | 1919-01-15 | 1 | ||
| SOUD ČS. VOJENSKÝCH ČÁSTÍ DÁLNÉHO VÝCHODU | Russia | institution | 1919-03-24 | 2 | |||
| TECHNICKÉ SKLADY ČS. VOJSKA | Russia | institution | 1919-07-15 | 1 | |||
| TÝLOVÉ INTENDANTSTVO ČS. VOJSKA | Russia | institution | 1919-01-20 | 1 | |||
| TŘETÍ ČS. DIVIZNÍ LAZARET | 3.0 | Russia | institution | 1919-02-09 | 1 | ||
| TŘETÍ ČS. SANITNÍ VLAK [II] | 3.0 | II | Russia | institution | 1918-10-12 | 1 | |
| TŘETÍ ČS. SANITNÍ VLAK [I] | 3.0 | I | Russia | institution | 1918-03-26 | 1 | |
| TŘETÍ ČS. VOJENSKÁ NEMOCNICE | 3.0 | Russia | institution | 1919-03-16 | 1 | ||
| TŘETÍ ČS. ZVĚROLÉKAŘSKÁ NEMOCNICE | 3.0 | Russia | institution | 1919-02-01 | 1 | ||
| VOJENSKÝ SOUD 1. ČS. DIVIZE | Russia | institution | 1919-03-15 | 2 | |||
| VOJENSKÝ SOUD 2. ČS. DIVIZE | Russia | institution | 1919-03-15 | 2 | |||
| VOJENSKÝ SOUD 3. ČS. DIVIZE | Russia | institution | 1919-03-15 | 2 | |||
| ZÁLOŽNÍ AUTOMOBILNÍ PARK ČS. VOJSKA | Russia | institution | 1919-01-01 | 1 | |||
| ÚŘAD AUTOMOBILNÍ SLUŽBY ČS. VOJSKA | Russia | institution | 1919-01-01 | 1 | |||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY 1. ČS. DIVIZE [II] | II | Russia | institution | 1918-07-07 | 1 | ||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY 1. ČS. DIVIZE [I] | I | Russia | institution | 1917-11-15 | 1 | ||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY 2. ČS. DIVIZE [II] | II | Russia | institution | 1918-10-07 | 1 | ||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY 2. ČS. DIVIZE [I] | I | Russia | institution | 1917-11-15 | 1 | ||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY 3. ČS. DIVIZE | Russia | institution | 1919-02-01 | 1 | |||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY VÝCHODNÍHO ODDÍLU ČS. SBORU | Russia | institution | 1918-05-16 | 1 | |||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY ČS. NÁHRADNÍCH JEDNOTEK | Russia | institution | 1917-11-15 | 1 | |||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY ČS. SBORU | Russia | institution | 1917-11-15 | 1 | |||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY ČS. SBORU V POLI | Russia | institution | 1918-11-27 | 1 | |||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY ČS. SBORU V TÝLU | Russia | institution | 1918-11-27 | 1 | |||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY ČS. VELITELSTVÍ DÁLNÉHO VÝCHODU | Russia | institution | 1919-05-30 | 1 | |||
| ÚŘAD ZDRAVOTNICKÉ SLUŽBY ŠTÁBU ČS. VOJSKA | Russia | institution | 1919-03-04 | 1 | |||
| ČS. POLNÍ POŠTA [I] | I | Russia | institution | 1918-09-09 | 1 | ||
| ČS. POSÁDKOVÁ NEMOCNICE VE VLADIVOSTOKU | Russia | institution | 1920-08-05 | 1 | |||
| ČS. VOJENSKÁ NEMOCNICE V JEKATĚRINBURGU | Russia | institution | 1918-08-27 | 1 | |||
| ČS. VOJENSKÁ NEMOCNICE V OMSKU | Russia | institution | 1918-08-20 | 1 | |||
| ČS. VOJENSKÁ NEMOCNICE V PETROPAVLOVSKU | Russia | institution | 1918-08-07 | 1 | |||
| ČS. VOJENSKÁ NEMOCNICE V TOMSKU | Russia | institution | 1918-11-01 | 1 | |||
| ČS. VOJENSKÁ NEMOCNICE V ČELJABINSKU | Russia | institution | 1918-07-28 | 1 | |||
| ČS. VOJENSKÁ NEMOCNICE VE ZLATOUSTU | Russia | institution | 1918-10-19 | 1 | |||
| ČS. VOJENSKÁ SPOŘITELNA | Russia | institution | 1919-01-08 | 1 | |||
| ČS. ZDRAVOTNICKÝ INSPEKTORÁT DÁLNÉHO VÝCHODU | Russia | institution | 1918-11-27 | 1 | |||
| ČTVRTÁ ČS. VOJENSKÁ NEMOCNICE [II] | 4.0 | II | Russia | institution | 1920-03-16 | 1 | |
| ČTVRTÁ ČS. VOJENSKÁ NEMOCNICE [I] | 4.0 | I | Russia | institution | 1919-04-09 | 1 | |
| ČTVRTÝ ČS. NEMOCNIČNÍ VLAK | 4.0 | Russia | institution | 1919-01-20 | 1 | ||
| ČTVRTÝ ČS. SANITNÍ VLAK | 4.0 | Russia | institution | 1918-05-26 | 1 | ||
| ŠESTÁ ČS. VOJENSKÁ NEMOCNICE | 6.0 | Russia | institution | 1919-01-09 | 1 | ||
| DESÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 10.0 | Russia | transport | 1 | |||
| DEVATENÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTI | Russia | transport | 1 | ||||
| DEVÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 9.0 | Russia | transport | 1 | |||
| DRUHÝ ČS. LODNÍ TRANSPORT DO VLASTI | 2.0 | Russia | transport | 1 | |||
| DRUHÝ ČS. ZVLÁŠTNÍ LODNÍ TRANSPORT DO VLASTI | 2.0 | Russia | transport | 1920-07-10 | 1 | ||
| DVACÁTÝ DEVÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 29.0 | Russia | transport | 1 | |||
| DVACÁTÝ DRUHÝ ČS. LODNÍ TRANSPORT DO VLASTI | 22.0 | Russia | transport | 1 | |||
| DVACÁTÝ OSMÝ ČS. LODNÍ TRANSPORT DO VLASTI | 28.0 | Russia | transport | 1 | |||
| DVACÁTÝ PRVNÍ ČS. LODNÍ TRANSPORT DO VLASTI | 21.0 | Russia | transport | 1 | |||
| DVACÁTÝ PÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 25.0 | Russia | transport | 1 | |||
| DVACÁTÝ SEDMÝ ČS. LODNÍ TRANSPORT DO VLASTI | 27.0 | Russia | transport | 1 | |||
| DVACÁTÝ TŘETÍ ČS. LODNÍ TRANSPORT DO VLASTI | 23.0 | Russia | transport | 1 | |||
| DVACÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 20.0 | Russia | transport | 1 | |||
| DVACÁTÝ ČTVRTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 24.0 | Russia | transport | 1 | |||
| DVACÁTÝ ŠESTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 26.0 | Russia | transport | 1 | |||
| DVANÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 12.0 | Russia | transport | 1 | |||
| JEDENÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 11.0 | Russia | transport | 1 | |||
| OSMNÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTI | Russia | transport | 1 | ||||
| OSMÝ ČS. LODNÍ TRANSPORT DO VLASTI | 8.0 | Russia | transport | 1 | |||
| PATNÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTI | Russia | transport | 1 | ||||
| PRVNÍ ČS. LODNÍ TRANSPORT DO VLASTI | 1.0 | Russia | transport | 1 | |||
| PRVNÍ ČS. ZVLÁŠTNÍ LODNÍ TRANSPORT DO VLASTI | 1.0 | Russia | transport | 1920-07-06 | 1 | ||
| PÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 5.0 | Russia | transport | 1 | |||
| SEDMNÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTI | Russia | transport | 1 | ||||
| SEDMÝ ČS. LODNÍ TRANSPORT DO VLASTI | 7.0 | Russia | transport | 1 | |||
| TŘETÍ ČS. LODNÍ TRANSPORT DO VLASTI | 3.0 | Russia | transport | 1 | |||
| TŘETÍ ČS. ZVLÁŠTNÍ LODNÍ TRANSPORT DO VLASTI | 3.0 | Russia | transport | 1920-07-13 | 1 | ||
| TŘICÁTÝ DRUHÝ ČS. LODNÍ TRANSPORT DO VLASTI | 32.0 | Russia | transport | 1 | |||
| TŘICÁTÝ PRVNÍ ČS. LODNÍ TRANSPORT DO VLASTI | 31.0 | Russia | transport | 1 | |||
| TŘICÁTÝ PÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 35.0 | Russia | transport | 1 | |||
| TŘICÁTÝ TŘETÍ ČS. LODNÍ TRANSPORT DO VLASTI | 33.0 | Russia | transport | 1 | |||
| TŘICÁTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 30.0 | Russia | transport | 1 | |||
| TŘICÁTÝ ČTVRTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 34.0 | Russia | transport | 1 | |||
| TŘICÁTÝ ŠESTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 36.0 | Russia | transport | 1 | |||
| TŘINÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTI | Russia | transport | 1 | ||||
| ČTRNÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTI | Russia | transport | 1 | ||||
| ČTVRTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 4.0 | Russia | transport | 1 | |||
| ČTVRTÝ ČS. ZVLÁŠTNÍ LODNÍ TRANSPORT DO VLASTI | 4.0 | Russia | transport | 1920-07-17 | 1 | ||
| ŠESTNÁCTÝ ČS. LODNÍ TRANSPORT DO VLASTI | Russia | transport | 1 | ||||
| ŠESTÝ ČS. LODNÍ TRANSPORT DO VLASTI | 6.0 | Russia | transport | 1 | |||
| AUTOMOBILNÍ ODDÍL 1. ČS. DIVIZE | Russia | unit | 1919-02-15 | 0 | |||
| AUTOMOBILNÍ ODDÍL 2. ČS. DIVIZE | Russia | unit | 1919-02-15 | 0 | |||
| AUTOMOBILNÍ ODDÍL 3. ČS. DIVIZE | Russia | unit | 1919-02-15 | 0 | |||
| DESÁTÝ ČS. PĚŠÍ PLUK | 10.0 | Russia | unit | 1918-08-20 | 1 | ||
| DESÁTÝ ČS. STŘELECKÝ PLUK [II] | 10.0 | II | Russia | unit | 1918-10-07 | 1 | |
| DESÁTÝ ČS. STŘELECKÝ PLUK [I] | 10.0 | I | Russia | unit | 1918-07-12 | 1 | |
| DEVÁTÝ ČS. PĚŠÍ PLUK | 9.0 | Russia | unit | 1918-08-20 | 1 | ||
| DEVÁTÝ ČS. STŘELECKÝ PLUK [II] | 9.0 | II | Russia | unit | 1918-10-07 | 1 | |
| DEVÁTÝ ČS. STŘELECKÝ PLUK [I] | 9.0 | I | Russia | unit | 1918-08-15 | 1 | |
| DOPRAVNÍ ODDĚLENÍ ČS. VOJSKA | Russia | unit | 1919-02-01 | 1 | |||
| DRUHÁ ČS. DĚLOSTŘELECKÁ BRIGÁDA | 2.0 | Russia | unit | 1917-11-26 | 1 | ||
| DRUHÁ ČS. SAMOSTATNÁ TECHNICKÁ ROTA [II] | 2.0 | II | Russia | unit | 1918-12-12 | 1 | |
| DRUHÁ ČS. SAMOSTATNÁ TECHNICKÁ ROTA [I] | 2.0 | I | Russia | unit | 1917-09-01 | 1 | |
| DRUHÁ ČS. STŘELECKÁ BRIGÁDA | 2.0 | Russia | unit | 1917-09-12 | 1 | ||
| DRUHÁ ČS. STŘELECKÁ DIVIZE [III] | 2.0 | III | Russia | unit | 1918-10-07 | 2 | |
| DRUHÁ ČS. STŘELECKÁ DIVIZE [II] | 2.0 | II | Russia | unit | 1918-09-03 | 2 | |
| DRUHÁ ČS. STŘELECKÁ DIVIZE [I] | 2.0 | I | Russia | unit | 1917-10-04 | 2 | |
| DRUHÁ ČS. TECHNICKÁ ROTA | 2.0 | Russia | unit | 1918-09-02 | 0 | ||
| DRUHÝ ODBOR ŠTÁBU ČS. VOJSKA | 2.0 | Russia | unit | 1919-02-01 | 1 | ||
| DRUHÝ ČS. JEZDECKÝ PLUK | 2.0 | Russia | unit | 1918-10-04 | 1 | ||
| DRUHÝ ČS. LEHKÝ DĚLOSTŘELECKÝ PLUK | 2.0 | Russia | unit | 1919-04-25 | 1 | ||
| DRUHÝ ČS. MUNIČNÍ ODDÍL | 2.0 | Russia | unit | 1919-04-24 | 1 | ||
| DRUHÝ ČS. NÁHRADNÍ PLUK | 2.0 | Russia | unit | 1917-11-29 | 1 | ||
| DRUHÝ ČS. POCHODOVÝ PRAPOR | 2.0 | Russia | unit | 1918-11-20 | 1 | ||
| DRUHÝ ČS. PĚŠÍ PLUK | 2.0 | Russia | unit | 1918-08-20 | 1 | ||
| DRUHÝ ČS. SAMOSTATNÝ DĚLOSTŘELECKÝ ODDÍL | 2.0 | Russia | unit | 1917-09-20 | 1 | ||
| DRUHÝ ČS. SAMOSTATNÝ JEZDECKÝ ODDÍL | 2.0 | Russia | unit | 1918-07-09 | 1 | ||
| DRUHÝ ČS. SAMOSTATNÝ MOŽDÍŘOVÝ MUNIČNÍ ODDÍL | 2.0 | Russia | unit | 1917-12-18 | 0 | ||
| DRUHÝ ČS. SAMOSTATNÝ MOŽDÍŘOVÝ ODDÍL | 2.0 | Russia | unit | 1917-12-18 | 1 | ||
| DRUHÝ ČS. SAMOSTATNÝ MUNIČNÍ ODDÍL | 2.0 | Russia | unit | 1917-09-20 | 1 | ||
| DRUHÝ ČS. STŘELECKÝ PLUK [III] | 2.0 | III | Russia | unit | 1918-10-07 | 1 | |
| DRUHÝ ČS. STŘELECKÝ PLUK [II] | 2.0 | II | Russia | unit | 1917-07-30 | 1 | |
| DRUHÝ ČS. STŘELECKÝ PLUK [I] | 2.0 | I | Russia | unit | 1916-05-19 | 1 | |
| DRUHÝ ČS. TĚŽKÝ DĚLOSTŘELECKÝ ODDÍL | 2.0 | Russia | unit | 1919-04-25 | 1 | ||
| DVANÁCTÝ ČS. PĚŠÍ PLUK | 12.0 | Russia | unit | 1918-08-20 | 1 | ||
| DVANÁCTÝ ČS. STŘELECKÝ PLUK [III] | 12.0 | III | Russia | unit | 1919-02-09 | 1 | |
| DVANÁCTÝ ČS. STŘELECKÝ PLUK [II] | 12.0 | II | Russia | unit | 1918-10-07 | 1 | |
| DVANÁCTÝ ČS. STŘELECKÝ PLUK [I] | 12.0 | I | Russia | unit | 1918-07-15 | 1 | |
| INSPEKTORÁT DĚLOSTŘELECTVA ČS. SBORU | Russia | unit | 1917-12-01 | 1 | |||
| INSPEKTORÁT NÁHRADNÍCH JEDNOTEK ČS. SBORU | Russia | unit | 1917-12-15 | 2 | |||
| JEDENÁCTÝ ČS. PĚŠÍ PLUK | 11.0 | Russia | unit | 1918-08-20 | 1 | ||
| JEDENÁCTÝ ČS. STŘELECKÝ PLUK [II] | 11.0 | II | Russia | unit | 1918-12-18 | 1 | |
| JEDENÁCTÝ ČS. STŘELECKÝ PLUK [I] | 11.0 | I | Russia | unit | 1918-07-15 | 1 | |
| JEKATĚRINBURSKÁ SKUPINA ČS. VOJSKA | Russia | unit | 1918-07-07 | 1 | |||
| LETECKÝ ODDÍL 1. ČS. DIVIZE | Russia | unit | 1918-08-14 | 1 | |||
| LETECKÝ ODDÍL 2. ČS. DIVIZE | Russia | unit | 1918-09-26 | 1 | |||
| LETECKÝ ODDÍL ČS. VOJSKA | Russia | unit | 1919-06-02 | 1 | |||
| LIKVIDAČNÍ ODBOR ŠTÁBU ČS. VOJSKA | Russia | unit | 1919-11-01 | 1 | |||
| NÁHRADNÍ PRAPOR 1. ČS. DIVIZE | Russia | unit | 1918-09-30 | 0 | |||
| NÁHRADNÍ PRAPOR 1. ČS. PĚŠÍHO PLUKU | Russia | unit | 1917-05-30 | 1 | |||
| NÁHRADNÍ PRAPOR 2. ČS. PĚŠÍHO PLUKU. | Russia | unit | 1917-06-19 | 1 | |||
| NÁHRADNÍ PRAPOR 3. ČS. PĚŠÍHO PLUKU | Russia | unit | 1917-06-25 | 1 | |||
| NÁHRADNÍ PRAPOR 4. ČS. PĚŠÍHO PLUKU | Russia | unit | 1917-06-26 | 1 | |||
| NÁHRADNÍ PRAPOR 5. ČS. PĚŠÍHO PLUKU | Russia | unit | 1917-07-13 | 1 | |||
| NÁHRADNÍ PRAPOR 6. ČS. PĚŠÍHO PLUKU | Russia | unit | 1917-07-13 | 1 | |||
| NÁHRADNÍ PRAPOR 7. ČS. PĚŠÍHO PLUKU | Russia | unit | 1917-07-13 | 1 | |||
| NÁHRADNÍ PRAPOR 8. ČS. PĚŠÍHO PLUKU | Russia | unit | 1917-07-13 | 1 | |||
| OSMÝ ČS. PĚŠÍ PLUK [II] | 8.0 | II | Russia | unit | 1918-08-20 | 1 | |
| OSMÝ ČS. PĚŠÍ PLUK [I] | 8.0 | I | Russia | unit | 1917-07-21 | 1 | |
| OSMÝ ČS. STŘELECKÝ PLUK [II] | 8.0 | II | Russia | unit | 1918-10-07 | 1 | |
| OSMÝ ČS. STŘELECKÝ PLUK [I] | 8.0 | I | Russia | unit | 1917-08-15 | 1 | |
| PENZENSKÁ SKUPINA ČS. VOJSKA | Russia | unit | 1918-05-29 | 1 | |||
| PRVNÍ ODBOR ŠTÁBU ČS. VOJSKA | 1.0 | Russia | unit | 1919-02-01 | 1 | ||
| PRVNÍ ČS. DĚLOSTŘELECKÁ BRIGÁDA | 1.0 | Russia | unit | 1917-11-05 | 1 | ||
| PRVNÍ ČS. JEZDECKÝ PLUK | 1.0 | Russia | unit | 1918-07-01 | 1 | ||
| PRVNÍ ČS. LEHKÝ DĚLOSTŘELECKÝ PLUK | 1.0 | Russia | unit | 1919-04-25 | 1 | ||
| PRVNÍ ČS. MUNIČNÍ ODDÍL | 1.0 | Russia | unit | 1919-04-24 | 1 | ||
| PRVNÍ ČS. NÁHRADNÍ PLUK | 1.0 | Russia | unit | 1917-10-29 | 1 | ||
| PRVNÍ ČS. NÁHRADNÍ STŘELECKÝ PLUK | 1.0 | Russia | unit | 1918-09-07 | 1 | ||
| PRVNÍ ČS. POCHODOVÝ PRAPOR | 1.0 | Russia | unit | 1918-09-18 | 1 | ||
| PRVNÍ ČS. PRACOVNÍ PRAPOR [II] | 1.0 | II | Russia | unit | 1919-03-04 | 1 | |
| PRVNÍ ČS. PĚŠÍ PLUK | 1.0 | Russia | unit | 1918-08-20 | 1 | ||
| PRVNÍ ČS. SAMOSTATNÁ TECHNICKÁ ROTA | 1.0 | Russia | unit | 1917-10-29 | 1 | ||
| PRVNÍ ČS. SAMOSTATNÝ DĚLOSTŘELECKÝ ODDÍL | 1.0 | Russia | unit | 1917-09-05 | 1 | ||
| PRVNÍ ČS. SAMOSTATNÝ JEZDECKÝ ODDÍL | 1.0 | Russia | unit | 1918-07-03 | 1 | ||
| PRVNÍ ČS. SAMOSTATNÝ MOŽDÍŘOVÝ MUNIČNÍ ODDÍL | 1.0 | Russia | unit | 1917-12-18 | 0 | ||
| PRVNÍ ČS. SAMOSTATNÝ MOŽDÍŘOVÝ ODDÍL | 1.0 | Russia | unit | 1917-12-18 | 1 | ||
| PRVNÍ ČS. SAMOSTATNÝ MUNIČNÍ ODDÍL | 1.0 | Russia | unit | 1917-10-10 | 1 | ||
| PRVNÍ ČS. SAMOSTATNÝ ÚDERNÝ PRAPOR | 1.0 | Russia | unit | 1918-10-17 | 1 | ||
| PRVNÍ ČS. STŘELECKÁ BRIGÁDA [I] | 1.0 | I | Russia | unit | 1917-09-12 | 1 | |
| PRVNÍ ČS. STŘELECKÁ DIVIZE [II] | 1.0 | II | Russia | unit | 1918-07-07 | 2 | |
| PRVNÍ ČS. STŘELECKÁ DIVIZE [I] | 1.0 | I | Russia | unit | 1917-09-05 | 2 | |
| PRVNÍ ČS. STŘELECKÝ PLUK [III] | 1.0 | III | Russia | unit | 1918-10-07 | 1 | |
| PRVNÍ ČS. STŘELECKÝ PLUK [II] | 1.0 | II | Russia | unit | 1917-07-30 | 1 | |
| PRVNÍ ČS. STŘELECKÝ PLUK [I] | 1.0 | I | Russia | unit | 1916-05-19 | 1 | |
| PRVNÍ ČS. TECHNICKÁ ROTA | 1.0 | Russia | unit | 1918-09-03 | 0 | ||
| PRVNÍ ČS. TĚŽKÝ DĚLOSTŘELECKÝ ODDÍL | 1.0 | Russia | unit | 1919-04-25 | 1 | ||
| PÁTÝ ODBOR ŠTÁBU ČS. VOJSKA | 5.0 | Russia | unit | 1919-03-04 | 1 | ||
| PÁTÝ ČS. PĚŠÍ PLUK [II] | 5.0 | II | Russia | unit | 1918-08-20 | 1 | |
| PÁTÝ ČS. PĚŠÍ PLUK [I] | 5.0 | I | Russia | unit | 1917-07-21 | 1 | |
| PÁTÝ ČS. STŘELECKÝ PLUK [II] | 5.0 | II | Russia | unit | 1918-10-07 | 1 | |
| PÁTÝ ČS. STŘELECKÝ PLUK [I] | 5.0 | I | Russia | unit | 1917-08-15 | 1 | |
| RUSKO-ČESKÝ STŘELECKÝ PLUK | Russia | unit | 1918-08-22 | 1 | |||
| SEDMÝ ODBOR ŠTÁBU ČS. VOJSKA | 7.0 | Russia | unit | 1919-02-01 | 1 | ||
| SEDMÝ ČS. PĚŠÍ PLUK [I] | 7.0 | I | Russia | unit | 1917-07-21 | 1 | |
| SEDMÝ ČS. STŘELECKÝ PLUK [II] | 7.0 | II | Russia | unit | 1918-10-07 | 1 | |
| SEDMÝ ČS. STŘELECKÝ PLUK [I] | 7.0 | I | Russia | unit | 1917-08-15 | 1 | |
| SEVEROZÁPADNÍ SKUPINA ČS. VOJSKA | Russia | unit | 1918-06-10 | 1 | |||
| SIBIŘSKÁ SKUPINA ČS. VOJSKA | Russia | unit | 1918-05-25 | 1 | |||
| SLOUČENÝ PRAPOR 1. ČS. STŘELECKÉHO PLUKU | Russia | unit | 1917-07-24 | 1 | |||
| SLOUČENÝ PRAPOR 2. ČS. STŘELECKÉHO PLUKU | Russia | unit | 1917-07-23 | 1 | |||
| SLOUČENÝ PRAPOR 3. ČS. STŘELECKÉHO PLUKU | Russia | unit | 1917-07-23 | 1 | |||
| STRÁŽNÍ ROTA ŠTÁBU ČS. VOJSKA | Russia | unit | 1919-02-01 | 0 | |||
| TŘETÍ ČS. LEHKÝ DĚLOSTŘELECKÝ PLUK | 3.0 | Russia | unit | 1919-04-25 | 1 | ||
| TŘETÍ ČS. MUNIČNÍ ODDÍL | 3.0 | Russia | unit | 1919-04-25 | 1 | ||
| TŘETÍ ČS. PĚŠÍ PLUK | 3.0 | Russia | unit | 1918-08-20 | 1 | ||
| TŘETÍ ČS. SAMOSTATNÁ TECHNICKÁ ROTA | 3.0 | Russia | unit | 1918-10-30 | 1 | ||
| TŘETÍ ČS. STŘELECKÁ BRIGÁDA | 3.0 | Russia | unit | 1917-10-04 | 1 | ||
| TŘETÍ ČS. STŘELECKÁ DIVIZE | 3.0 | Russia | unit | 1919-02-01 | 2 | ||
| TŘETÍ ČS. STŘELECKÝ PLUK [III] | 3.0 | III | Russia | unit | 1918-10-07 | 1 | |
| TŘETÍ ČS. STŘELECKÝ PLUK [II] | 3.0 | II | Russia | unit | 1917-07-30 | 1 | |
| TŘETÍ ČS. STŘELECKÝ PLUK [I] | 3.0 | I | Russia | unit | 1917-04-09 | 1 | |
| TŘETÍ ČS. TĚŽKÝ DĚLOSTŘELECKÝ ODDÍL | 3.0 | Russia | unit | 1919-04-25 | 1 | ||
| URALSKÁ SKUPINA ČS. VOJSKA | Russia | unit | 1918-07-25 | 1 | |||
| VELITELSTVÍ DĚLOSTŘELECTVA 1. ČS. DIVIZE | Russia | unit | 1919-04-24 | 1 | |||
| VELITELSTVÍ DĚLOSTŘELECTVA 2. ČS. DIVIZE | Russia | unit | 1919-04-24 | 1 | |||
| VELITELSTVÍ DĚLOSTŘELECTVA 3. ČS. DIVIZE | Russia | unit | 1919-04-25 | 1 | |||
| VLADIVOSTOCKÁ SKUPINA ČS. VOJSKA | Russia | unit | 1918-06-29 | 1 | |||
| VOZATAJSKÁ ROTA ŠTÁBU ČS. VOJSKA | Russia | unit | 1919-02-01 | 0 | |||
| VÝCHODNÍ ODDÍL ČS. SBORU [II] | II | Russia | unit | 1918-09-01 | 1 | ||
| VÝCHODNÍ ODDÍL ČS. SBORU [I] | I | Russia | unit | 1918-05-16 | 1 | ||
| VÝCHODNÍ SKUPINA ČS. VOJSKA | Russia | unit | 1918-06-10 | 1 | |||
| ZÁPADNÍ SKUPINA ČS. VOJSKA | Russia | unit | 1918-06-10 | 1 | |||
| ČELJABINSKÁ SKUPINA ČS. VOJSKA | Russia | unit | 1918-05-25 | 1 | |||
| ČESKÁ DRUŽINA | Russia | unit | 1914-08-28 | 1 | |||
| ČS. ARMÁDNÍ SBOR [I] | I | Russia | unit | 1917-11-05 | 3 | ||
| ČS. AUTOMOBILNÍ ODDÍL | Russia | unit | 1918-06-30 | 1 | |||
| ČS. AUTOMOBILNÍ ROTA | Russia | unit | 1918-09-26 | 0 | |||
| ČS. AVIATICKÝ A AUTOMOBILNÍ ODDÍL | Russia | unit | 1918-02-17 | 1 | |||
| ČS. KURGANSKÝ POCHODOVÝ PRAPOR | Russia | unit | 1918-06-20 | 0 | |||
| ČS. LODNÍ ODDÍL | Russia | unit | 1919-11-15 | 0 | |||
| ČS. NÁHRADNÍ BRIGÁDA | Russia | unit | 1917-10-28 | 1 | |||
| ČS. NÁHRADNÍ DĚLOSTŘELECKÝ ODDÍL | Russia | unit | 1917-08-03 | 1 | |||
| ČS. NÁHRADNÍ ODDÍL | Russia | unit | 1916-05-26 | 1 | |||
| ČS. NÁHRADNÍ PRAPOR [II] | II | Russia | unit | 1918-06-21 | 1 | ||
| ČS. NÁHRADNÍ PRAPOR [I] | I | Russia | unit | 1916-11-30 | 1 | ||
| ČS. NÁMOŘNÍ ODDÍL | Russia | unit | 1920-03-15 | 0 | |||
| ČS. ODDÍL OBRNĚNÝCH AUTOMOBILŮ | Russia | unit | 1919-08-20 | 0 | |||
| ČS. SAMOSTATNÁ DOPRAVNÍ ROTA | Russia | unit | 1919-04-15 | 1 | |||
| ČS. SAMOSTATNÁ RADIOTELEGRAFNÍ ROTA | Russia | unit | 1919-02-01 | 0 | |||
| ČS. SAMOSTATNÁ STROJÍRENSKÁ ROTA | Russia | unit | 1919-05-04 | 0 | |||
| ČS. SAMOSTATNÁ TELEGRAFNÍ ROTA | Russia | unit | 1918-01-07 | 0 | |||
| ČS. SAMOSTATNÝ ÚDERNÝ PRAPOR | Russia | unit | 1917-12-21 | 1 | |||
| ČS. STRÁŽNÍ PRAPOR | Russia | unit | 1919-05-10 | 1 | |||
| ČS. STŘELECKÁ BRIGÁDA. | Russia | unit | 1916-05-19 | 1 | |||
| ČS. STŘELECKÝ PLUK | Russia | unit | 1916-02-02 | 1 | |||
| ČS. TECHNICKÝ PRAPOR | Russia | unit | 1918-07-03 | 1 | |||
| ČS. VELITELSTVÍ DÁLNÉHO VÝCHODU | Russia | unit | 1918-09-26 | 2 | |||
| ČS. VOJENSKÝ TÁBOR V BORISPOLU | Russia | unit | 1917-05-30 | 1 | |||
| ČS. VOJENSKÝ TÁBOR VE VALCARTIERU | Russia | unit | 1920-06-13 | 1 | |||
| ČS. VOJSKO NA RUSI | Russia | unit | 1919-02-01 | 1 | |||
| ČTVRTÁ ČS. STŘELECKÁ BRIGÁDA | 4.0 | Russia | unit | 1917-10-04 | 1 | ||
| ČTVRTÝ ODBOR ŠTÁBU ČS. VOJSKA | 4.0 | Russia | unit | 1919-02-01 | 2 | ||
| ČTVRTÝ ČS. PĚŠÍ PLUK | 4.0 | Russia | unit | 1918-08-20 | 1 | ||
| ČTVRTÝ ČS. STŘELECKÝ PLUK [II] | 4.0 | II | Russia | unit | 1918-10-07 | 1 | |
| ČTVRTÝ ČS. STŘELECKÝ PLUK [I] | 4.0 | I | Russia | unit | 1917-09-05 | 1 | |
| ŠESTÝ ODBOR ŠTÁBU ČS. VOJSKA | 6.0 | Russia | unit | 1919-02-01 | 1 | ||
| ŠESTÝ ČS. PĚŠÍ PLUK [II] | 6.0 | II | Russia | unit | 1918-08-20 | 1 | |
| ŠESTÝ ČS. PĚŠÍ PLUK [I] | 6.0 | I | Russia | unit | 1917-07-21 | 1 | |
| ŠESTÝ ČS. STŘELECKÝ PLUK [II] | 6.0 | II | Russia | unit | 1918-10-07 | 1 | |
| ŠESTÝ ČS. STŘELECKÝ PLUK [I] | 6.0 | I | Russia | unit | 1917-08-15 | 1 |
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
| Module | Version | Role | Uncommitted |
|---|---|---|---|
| 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.py | 0.1 | Reference 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.py | 3 | Output & support | yes |
| 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
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
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:
- 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.
- 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.
- 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.
- 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.
- 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.py1.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:
- 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).
- 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 theexit_Nfix 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, andregimentconflating 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/finalis currentAll 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.py2.0.0 rebuilds the variable on the 1869 ladder, withpok.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);
_normnow 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 ownpol_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/finalnow reflects the fixed code — measured results in §E. A copy of the pre-fixdata/finalwas 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 quotingexit_Nis 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):
_DROP_TOKENSis a no-op:found -= _DROP_TOKENSruns after tokenisation, but the tokenizer only ever adds canonical codes, and no drop token is canonical, so nothing is ever removed.- 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). - The NaN guard only caught
floatNaN — butclean.normalize_missingturns missing/?cells intopd.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'sexit_N = 27,034is ~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číkmapping is undocumented. The Habsburg rules mappodpraporcik→ 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 droppingpodporučík/rotmistrbut 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 separateVocational continuationlevel 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ě, barepokr.,hosp.,odb.,kadetka) — coverage is 96.7%, unmatched ≈3.5k with systematic classes recoverable by ~8 extra tokens. → Fixed 2026-09-02 (education.py2.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"→pokrdouble-count is real but tiny. Verified: 13 rows match bothsoc_demandpokr(e.g. "sociální demokrat"). Fix by skipping the baredemokrattoken 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_lookupkeeps the first of two same-named court districts in different okresy silently — print/log duplicates once so the ambiguity is auditable.add_birth_settlement_okresandadd_settlement_jarasbothreset_index(drop=True)— harmless in the notebook (RangeIndex) but a footgun for any caller with a meaningful index. - B7 · records_geo.py —
add_settlement_attrsunique-name test viaid(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
_normdoes not expandn./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 1910csslot — against the statedcs= 1910-name convention. Add ann.→nadexpansion in_norm(or aliases), and fix the curation row (put Nové Benátky incs, Benátky nad Jizerou incs_modern). "Česká Třebová" (21) names a town that was not a 1910 court/okres — candidate forALIAS_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_REcould 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
(
dupcheck inbuild_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 thatadd_settlement_attrshas. - B13 · units.py —
pol.děl.pl.labels as generic "Artillery regiment" becauseděl.pl.precedespol.děl.inUNIT_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", nolibrary/, nodata/finalsplit, 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.pyv1.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í rotmistris "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 atreference/census/census_codebook.csvas 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
zakcannot strikezakopnik. First-match-wins resolves correctly in all checked cases. - Common scale / reset:
RANK_COMMONis name-anchored exactly as §G specifies;rank_resetpropagates<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_flagsdeduplicates per side keeping the highest tier; blocking on (year, initial) is consistent with both criteria requiring exact year. Numbers in CLAUDE.md verified againstoverlap_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-onlylocations.pyland, as documented. sum_okin 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
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_grouppair 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 thezámečník5-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. 3andodb. 2count years of a thing whose size is unknown.education_completeis<NA>across that whole branch rather than guessed, and noeducation_years_totalis 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. Ncounts years or classes.education_completeassumes 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.py2.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 ofSL(108 records, the largest unmatched token),ludovákandludová. 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_exactruns 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
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. Sopracovníkmarks 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čanappears 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
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.
Check 1 — the German link bridge (run, 2026-08-01)
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íkis 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ýandstavitel(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 — seeAUDIT_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.pynow 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_hiscoonly, so the entireoccupation_interwar_*family was unvalidated. That is precisely how a HISCO code absent from the authority survived:četnictvowas coded58210, 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ý adjunktis 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 whenrelationis 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-". Becausesplit_occupationstriesdualbeforehead, 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 onmlynářskýandsladovnický;hostinskýlooks like one of these but is a genuine noun and its duals are correct. Fixed byADJECTIVE_KEYSinoccupations.py1.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 thatOCCUPATION_CODING.mddocumented behaviour the code did not have: it gavemlynářský dělník → dělníkas theheadexample, when the string was in fact matchingdualand 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, includingakademický malíř(57 — an academically trained fine artist) andmalíř písma(78 — a sign painter). Likewisebrusič drahokamů(19, gem cutter as metal grinder) andzubní lékař(10, dentist as physician — thoughzubní lékařkawas 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.pylabelled a code from its ancestor group without backfilling the ancestry codes, so the 18 codes labelled above micro level carried blankmajor/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 keysdělníkandzřízenecget 99900, HISCLASS 13. Now 99900.- Status
43does not exist.abiturientandžákemitted it inoccupation_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ářifails:řiis not a palatalised plural. - Head-noun position. Within each coordinate phrase, leading adjectives are stripped and
the first remaining token is the head. So
studentdoes not match Řídící pracovníci ve studentských ubytovnách (headpracovníci→ Hotel managers), andobchodníkdoes not match Obchodní náměstci (headná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 arehajný,myslivec,příručí,soustružník,tkadlec,mlynář,kolář,bednář,cihlář,stolař,topič,holič,havíř,svrškařandhostinský. 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:
- 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íkis 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. - 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.
- 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.
- 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íkis glossed Landwirt; the term present here is Bauer, which is this project's gloss forsedlák— a key with 6 records. A bare LANDWIRT is absent from the 521-term list. So the bridge from this evidence torolníkruns through a Landwirt ≈ Bauer synonymy that the check assumes rather than shows. - 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.
- 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 forstrojní zámečníkagainst barezámečníkin 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) andknihvedoucí(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 —
politikandsprávce— still open, 2026-08-26.)run_czisco_check.pywrites theczisco_*columns but never thereviewcolumn, 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číkandsanitář(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ůandProvozní 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 Bohemianzámečníkgenuinely 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,204strojní zámečníkrecords — 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 barezá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ánuandhajný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 toadjunktandšafářwas carrying him into management. The four trainee grades go further —hospodářský praktikant(10),hospodářský asistent(10),lesní praktikant(9) andlesní 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íkwould have given them anyway — the qualifier still buys the recorded modifier, not a class. - ~~
skladník(140) andmlékař(14)~~ adjudicated 2026-08-26, and they split.skladníkis 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
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.pywas 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, andsecondaryis the parser's own "unspecified" bucket. - Two school kinds with no token:
HandwerkerschuleandNotschule. Handwerkerschuleis folded intotrade. 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
unparsedwould 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
INSTITUTIONStable. 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 %
unparsedrecords 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.