3 Layered OCR, and the Kirch reprocess
The page grammar, the prompt, and the model are three separate decisions. Kirch was processed as if they were one.
A page is tagged in the universal page language. The prompt that asks for those tags is built in layers, and each layer is stamped on the page. The model is chosen after that prompt exists, from a measurement on a real page. Kirch has none of the three: its pages were read in August 2026 by gemini-3.6-flash and qwen3.5:cloud, from templates that name elements page.dtd v4.0 does not admit.
Compose the prompt from the page language outward, stamp the layers, pick the model from a measured page — and rebuild Kirch on that pattern, as calendars and scholarly books, before any of its 4,668 pages is read again.
The grammar itself is the page language. This chapter is what changed in the prompt and the model choice, and what that implies for Kirch.
3.1 One page, two prompts
KGW 3, printed page 102, was read twice on 28 September 2026. The facsimile has a running header 102, line counts 45 and 10 in the right margin, and a gloss on Brahe in the left margin beside Denique eadem.
The first prompt was the older Kepler overlay, handed to the model as a single file. Kimi returned the gloss with type="marginal-note", which is not a word of the margin list, and it missed the printed header. Qwen3.8 returned the gloss as type="note". Gemini 3.8, on that same older file, returned XML that failed to parse. qwen3.5:cloud answered HTTP 410. deepseek-v4-pro:cloud answered HTTP 400: the model does not accept an image.
The second prompt was composed. ocr_runner.cure_config.compose stacks five layers, and stops at the last one that exists:
- the page-language fragment, filled with this collection’s margin types;
- the collection base;
- the volume note, when the volume has one;
- the engine note;
- the volume-and-engine note.
Kimi then ran as kepler-ocr@739ec396938e with layers page-language, base, kimi. Gemini 3.8 ran as kepler-ocr@ef4f29445dd4 with page-language, base, gemini38. Both returned the header 102, both line counts as line-number on the right, and the gloss as type="topic" on the left. The audit reported 0 defects and 0 blockers. Kimi took 9.5 seconds; Gemini 3.8 took 11.0 seconds and $0.0074 at the promotional rate.
The same page, after the prompt was composed.
<meta partitur="kepler-ocr@739ec396938e"
layers="page-language,base,kimi"
overlay="kepler-ocr.kimi.j2"
model="kimi-k2.7-code:cloud"/>Kimi 0 defects, both line counts, gloss typed topic. Gemini 3.8 the same tags, $0.0074. Rapid-MLX local qwen3.8:27b-mlx via the server on port 5138: valid tags, the line count 45 absent, 75 seconds.
The hash is the first twelve hex digits of the composed instruction, with the page variables left as placeholders. It is the same for every page of one model and volume, and it changes when any layer changes. pdai-partitur check refuses a model that composes base-only or whose overlay names a tag outside the grammar.
Local qwen3.8:27b-mlx remains the engine for harvest and for a run that must stay on this machine. Rapid-MLX (rapid-mlx/Qwen3.8-27B-4bit-MTP-MLX, prefill step 8192, vision cap 800,000 pixels, thinking off, one request in flight) is a route for that same model. On this page it was legal and slower, and it dropped a line count the composed cloud prompts kept. It is a measured option for a local pilot, and the printed-page default stays kimi-k2.7-code:cloud, with gemini-3.8-flash only when Kimi fails on that page.
3.2 What Kirch actually is
The steering catalog calls the collection Kirch Astronomical Correspondence and points it at generic page.dtd and ai-scholar process. The seventeen biblio.xml files name the Acta Calendariographica series edited around Gottfried Kirch and the early modern Schreibkalender: Klaus-Dieter Herbst’s Forschungsberichte and Biobibliographisches Handbuch der Kalendermacher, Anja Groß, Der kompilierte Schreibkalender, the Kalenderreihen, Johann Christoph Sturm’s Finsternissen-Calender, and Herbst’s Gottfried Kirch: Astronom, Kalendermacher, Pietist, Frühaufklärer.
There is no collections/kirch/ tree, no collection wrapper, and no partitur attribute on any page. The census of 28 September 2026 validated every page against page.dtd v4.0.
| Pages | Valid | Invalid | Malformed | |
|---|---|---|---|---|
| Kirch, 17 volumes | 4,668 | 2,267 (48.6%) | 2,327 | 74 |
The invalid pages fail in the vocabulary the August templates taught:
| What the files contain | Pages citing it | What the page language admits |
|---|---|---|
<section> inside <main> |
405 | p, figure, marginal, table |
<i>, <em>, <b>, <strong> |
254 | <hi rend="italic"> or rend="bold" |
<sup> |
191 | <hi rend="superscript"> |
<figure> with description before caption, or with <p> inside |
160 | (caption?, description?) |
<footnote> as a child of <page> |
142 | <footnotes><footnote>… |
broken & |
38 | a declared entity |
<fn> or <footer> with child elements |
7 | text |
type="catchword", type="table" |
2 | the closed margin list |
The former central template kirch-fraktur-ocr.j2 (retired on 29 September 2026; the Kirch prompts are now composed from collections/kirch/ai-instructions/) was explicit. It was scored 0.976 on a 35-page sample of volumes 0002, 0003, 0005, 0006, 0008, 0009 and 0010, and it tells the model to emit <section l="2">, a figure with <description> before <caption>, and a footnote. The sample score measured agreement with that template. The census measures the template against the grammar, and the template loses.
The same template was then used far outside that sample. Volumes 0011–0017 are the handbook, the essays on the Schreibkalender, and the Kirch biography: 3,434 pages, all stamped kirch-fraktur-ocr and gemini-3.6-flash. A Fraktur calendar prompt on a modern antiqua handbook is the wrong instruction, whatever the model.
| Volumes | What the biblio names | Pages | Valid | How they were read |
|---|---|---|---|---|
| 0005, 0007, 0008 | Kalenderreihen 1.6, 3.2, 1.4 | 438 | 287 | 0005 on qwen3.5:cloud; 0007 and 0008 on the Fraktur template |
| 0006, 0009, 0010 | Kalenderreihen 1.5, Sturm, 3.1 | 314 | 133 | Fraktur template and full_page_scholarly, mixed inside the volume |
| 0001, 0003, 0011, 0015 | Forschungsberichte | 1,022 | 453 | Fraktur template, except 0003 on full_page_scholarly |
| 0002 | Groß, Der kompilierte Schreibkalender | 110 | 22 | full_page_scholarly only — the lowest rate in the collection |
| 0004 | Forschungsberichte 4 | 108 | 106 | qwen3.5:cloud — the highest rate, and a retired model |
| 0012, 0014, 0016, 0017 | Handbuch der Kalendermacher | 1,932 | 877 | Fraktur template on every page |
| 0013 | Gottfried Kirch | 744 | 389 | Fraktur template on every page |
Engine stamps, collection-wide: 4,414 pages gemini-3.6-flash, 254 pages qwen3.5:cloud. Neither name is available for a new run. gemini-3.6-flash is outside the allowlist. qwen3.5:cloud was retired on 25 September 2026. A page that validates today was still produced by one of those two, and it carries no partitur hash, so a later audit cannot say which instruction wrote it.
3.3 How Kirch is reprocessed
The reprocess follows the document-grammar procedure: profile, grammar, layer, fit, run. Nothing is written to a volume before its gate passes.
Grammar per document. Each of the 17 documents has a document_profile.yaml that names its page classes (kalendarium, computus, entry, table, register-table, facsimile-text, inset-facsimile, show-through, and the default) and a wrapper DTD that adds only what the front matter attests. The Vision classifier assigns a class to each page (three votes, gemini-3.5-flash-lite); the geometry census breaks ties.
Layer per engine and class. The prompt is composed from the page language, the collection base, the volume layer, the engine overlay, and the class layer. Layers were trained on 46 pilot pages spread over all 17 documents.
Fit check. Every reading is compared with its facsimile by measurement only: DTD and audit, table rows and columns against the image census, text agreement with the Vision reading (Antiqua) or between two engine readings (Fraktur, mixed), figures, margins, and ink share (a leaf without ink must yield an empty <main/>). A page fits when no finding fails.
Pilot result (29 September 2026, 46 pages).
| Reading | Pages that fit |
|---|---|
Decision of the dual read (Kimi, gemini-3.8-flash when the fit check doubts Kimi) |
46 of 46 |
kimi-k2.7-code:cloud alone |
43 of 46 |
gemini-3.8-flash alone |
43 of 46 |
qwen3.8:27b-mlx on the Kalender volumes |
11 of 17 |
| Production XML before the reprocess | 10 of 17 on the document-grammar pages |
Kimi alone invents a calendar table on a faint page (0009 p. 60) and merges days into six rows on one register page (0007 p. 125). gemini-3.8-flash fits every Kalender page but is about three times slower. The two fail on different pages, so the decision reading is the measured winner, not either engine alone. The thresholds were calibrated on these pages; the per-volume gate below is the held-out check. Evidence: hub runs 84d67c2eed3c994bbf6f, 60ab00b1876fe63438bc, b131b7d26147f3dd7f00; Setting cure-kirch. A reproduction of a historical page inside a modern volume (inset-facsimile) is one <figure> without caption and description (owner decision, 29 September 2026).
Run. collections/kirch/scripts/chain_kirch.sh runs one volume at a time in the order modern volumes, Handbuch, Kalenderreihen. For each volume, python -m ocr_runner.pilot gate reads five random pages the pilot has not seen and the volume is skipped if any does not fit. Otherwise ocr_runner.dual_read --write --resume --escalate 3 runs in batches of 40 pages, and every overwritten XML is kept as .xml.v<n>. The chain waits while another OCR runner is alive and stops after the current batch when STOP appears in /Volumes/PDAI-1/tmp/kirch-ocr/. New pages are stamped with partitur, layers, and the engine that wrote them.
Letters stay out of this run. These volumes are not a correspondence register, and letters_resolved is not rebuilt from them.