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.

TipIn one sentence

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:

  1. the page-language fragment, filled with this collection’s margin types;
  2. the collection base;
  3. the volume note, when the volume has one;
  4. the engine note;
  5. 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.