16  Application

Stage 5: flagged pages only

16.1 Commands

pgrep -fl "cure_run|cure_learn|normalize_apply|ai-scholar process"    # must print nothing
.venv/bin/python -m ocr_runner.normalize_apply --collection <c> --doc <d> --apply [--only 127,347]
.venv/bin/python -m ocr_runner.cure_run --collection <c> --doc <d> --worklist \
    --max-pages 40 --max-minutes 90

normalize_apply --apply refuses unless the specimen gate passed for the same normalizer version. cure_run --worklist processes the document’s worklist from pdai-validity.

16.2 The worklist

The worklist holds only pages with verdict fail. For each page it names the next engine to try: the page walks the collection’s engine_chain, skipping engines already tried on it, including the one that produced the current XML. gemini-3.8-flash comes only after every engine of the chain has failed on the page. Pages with verdict warn are listed for review; pages with verdict ok never appear.

16.3 Runner guards

Guard Value (copernicus-prim) Effect
num_predict 4096 bounds a generation loop
repeat_penalty 1.1 reduces repeated units
reject codes LOOP-REP, TRUNC, SYN-WELLFORMED the answer is discarded, the next engine is tried
page budget 40 pages later pages are deferred
time budget 90 minutes later pages are deferred

Every overwritten page XML is kept as a backup next to it. Each engine answer is ranked by the audit (blockers, validity verdict); the best answer is written, and a page whose best answer is not better than the current XML keeps its XML.

16.4 Adjudication by audit

An engine answer is accepted by measurement only: the runner assesses each answer with the same validity check and structural audit as the diagnosis. A page is written when its verdict improves; it is accepted as repaired when the verdict is ok.

16.5 Billing

Every Gemini call of the runner is recorded in the ocr-billing DuckDB (agents/document-processing/ocr_billing.duckdb) with collection, document, page and template label. Before this method was set up, the billing path of ocr_runner/client.py pointed to a non-existent directory and failed silently; copernicus-prim has no billing rows for its earlier Gemini fallback calls.

16.6 In the Partitur

Stage apply (op pdai:cure_apply). Without the input apply: true the op runs the dry run (--dry-run, or the specimen for a normalizer) and reports the plan. It refuses to start when the gate did not pass or another processing agent is running.