The Method Stack

How Elitez read the core batch of ~40,000 timesheets — no cloud AI, no per-vendor templates, running entirely on the local fleet.

← Overlays  ·  Work done  ·  Core batch complete   Updated 8 Sep 2026

01The corpus, in numbers

Verified counts from the published shards and the extraction ledger.

52,014
timesheets read (11 shards)
221,459
rows extracted
3
vision models per page
98.2%
row recovery (null-matched)
73.2%
all-5-fields correct
0
rows sent to any cloud API

The core batch is complete. The core DWD timesheet batch is ~40,000 unique documents (38,389 processed; the active queue is fully drained). The larger 72,719 figure counts pages, not documents — multi-page sheets and render variants inflate the page total roughly 1.8×. Accuracy has plateaued at this level: a 4th model and consensus voting both measured lower, and five LoRA fine-tunes all lost to the plain union.

Accuracy is reported three ways, honestly: 98.2% of readable fields recovered, 73.2% of sheets with all five fields correct, 37.2% strict character-exact. We score only cells where a true value exists — a model is never rewarded or punished for a genuinely blank cell.

02The pipeline, end to end

Each sheet — a phone photo or a scanned PDF — passes through the same stack.

  1. Render & normalise. PDF pages rasterised at 150 dpi and scaled to ~2,000 px; photographs auto-rotated and deskewed only where a defect is detected — applied blindly, normalisation cost accuracy (see §6).
  2. Three-model vision union. The page is read independently by three local vision models; their answers are reconciled field-by-field (see §3). No single model is trusted alone.
  3. Second-pass geometry. The vision models return values but no coordinates. A separate Tesseract + OpenCV pass finds the real table grid and pins each extracted row to its actual pixels (see §4).
  4. Compute & reconcile hours. Worked hours are recomputed from time-in / time-out / break with shift-aware logic (~139,500 rows computed; ~60,000 blank totals filled; ~20,000 discrepancies flagged, never silently zeroed) and cross-checked against the sheet's own stated total.
  5. Classify & gate. A classifier confirms the page is actually a timesheet before its hours count — printed email threads and contracts are held out (see §8, learning of 4 Sep).
  6. Score, route & publish. ~50% of sheets clear automatically at high confidence; ~50% with model disagreement or a weekday mismatch (1,534 auto-flagged) route to an HR reviewer, whose corrections feed the next evaluation.

03Why three models, not one

Any single vision model hallucinates on a bad photo — a smudged "3" becomes an "8", a blank cell gets a confident guess. Three models rarely make the same mistake. We read every page with all three and reconcile by first-non-null agreement keyed on the canonical date: a field is filled from the first model that read it, and a model's correct blank read is respected rather than overwritten by another's guess.

ModelRole in the union
qwen2.5vl:7bPrimary reader — strongest on structured grids and printed digits.
mistral-small3.2:24bHeavyweight arbiter — best on messy handwriting; slowest, so used when GPUs are free.
richardyoung/olmocr2:7b-q8Document-specialist — trained for scans; catches rows the others miss.

Diversity beats size. A 4th model (gemma3) dropped all-5 accuracy to 72.3%, and consensus voting to 70.5%. What earns an ensemble slot is a genuinely different failure profile — shared-bias errors are exactly what voting cannot fix.

04Where the boxes come from

The overlays are not decoration and they are not invented. Vision models return text with no coordinates, so a second pass — Tesseract word-boxes (TSV) plus OpenCV horizontal/vertical line detection — reconstructs the actual table grid, and each reconciled row is assigned to the grid band it physically occupies. A row we could extract but not locate is drawn as an inferred band and marked as such. Green = row extracted and date resolved; amber = extracted but the date could not be resolved from the sheet.

05What actually moves accuracy

Every gain below is measured on a held-out split built after the change was designed — so these are generalisation, not tuning-set inflation.

LeverMeasured gainWhere it applies
Capture format: photo → scan~+36 ptthe single biggest lever — an operations change, not engineering
Row-level union across models+3.3 to +13 ptalways on — recovery everywhere
Weekday-constrained period inference+20 pt recoveryalways on, when the period is inferable
Cross-model abstention (K-of-N)+3.7 to +11 ptphotos only — it −7 pt on scans
Per-field K (abstain on total only)+9.5 ptcorpus-dependent — helps v3, hurts v4
Ensemble prompt-parity+7.6 pta silent prompt drift cost this, invisible in aggregates
Selective image normalisation+0.6 ptdefect-only; applied blindly, −4.9 pt
Deterministic post-check (break==total×60)+0.7 pton, safe by construction (12/12 precision)

06What does not work

Recorded so no one retries them blind. Each was tested and refuted on held-out data.

07How the system keeps improving itself

Seven mechanisms are installed so the pipeline accretes gains and keeps its own claims verifiable.

  1. Error-taxonomy-first loop. Every accuracy push begins by classifying errors (hallucinated / misread / missed) on held-out data — not by hypothesis. The two biggest gains both came from taxonomies; five plausible hypotheses tested without one were all refuted.
  2. Frozen-split discipline. Every change is validated on a split built after it was designed. A +11.4 pt tuning-set gain was +3.7 pt clean — ~3× inflation, caught by the discipline.
  3. Contamination gate. A content-hash audit across all splits runs before any cross-set number is quoted. Currently: zero duplicates spanning any two sets.
  4. Per-corpus config, not tuned constants. K-of-N, per-field K and normalisation are keyed on source type (photo vs scan). Three corpora gave three different optima — one global pipeline is guaranteed wrong somewhere.
  5. Ensemble prompt-parity assertion. Every union member must be on the same prompt version, asserted at assembly — a silent drift once cost 7.6 pt and was invisible in aggregates.
  6. Build-time transforms re-run by default. Canonicalisation re-applies over new data automatically — a step that silently stopped once cost 20% of the corpus.
  7. Artefact-verified agent work. Completion claims are checked against on-disk line counts, not exit codes — caught one false success and one silent stall in ~150 dispatches.

Where the frontier goes next. A photo-vs-scan router (both halves already exist); hard-example mining so ground truth grows where the model is weakest; a 4th diverse model only if it shows a different failure profile on the frozen set; and two narrow retargeted LoRAs (a row-detector head; scans-only) — each one run against the zero-shot bar, not a commitment.

08What we learned — the running log

Every item here changed the pipeline. Most were paid for in wrong answers first.

09Principles we won't give back