Skip to content

Eval harness: arithmetic-invariant switch and per-file progress - #1

Merged
asaptf merged 1 commit into
mainfrom
eval/invariant-switch-and-progress
Aug 10, 2026
Merged

Eval harness: arithmetic-invariant switch and per-file progress#1
asaptf merged 1 commit into
mainfrom
eval/invariant-switch-and-progress

Conversation

@asaptf

@asaptf asaptf commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Two eval-harness changes, both extracted from an experiment branch and re-authored clean for main. Harness-only — nothing under Sources/Extract changes, so library behaviour and the published API are untouched.

1. Run-level switch for the arithmetic invariant

EvalInvoice.validateInvariants() enforces sum(lineTotals) + tax ≈ grandTotal unconditionally. On a small local model that check rejects nearly every extraction — and a rejected extraction contributes no scored fields at all, so the report reads 0.0% (0/0) rather than a low accuracy. Hard failures vanish from the denominator instead of counting as misses.

That makes an A/B measure the gate rather than the extractor. Measured on 6 Factur-X documents with Qwen2.5-1.5B: 6/6 files ended in validationFailed, and the accuracy report scored nothing at all.

  • Config-spec key invariant=true|false (aliases arithmeticInvariant, arithmetic-invariant)
  • --invariant true|false on the accuracy path
  • Default stays true — existing reports keep their present meaning
  • Installed and restored per arm, so a compare run cannot leak the setting between arms
  • The mode is always printed in the report label (invariant=false note=arithmetic-invariant-off). A report whose numbers silently change meaning is worse than no report.

2. Per-file progress on stderr

AccuracyRunner now emits [arm i/n] relative/path (elapsed s) per file.

Model-backed runs take minutes per file and print nothing until the end, which makes a slow run indistinguishable from a hung one. A corpus run in this repo was left going for seven hours before it became clear it was making progress but nowhere near finishing — with no way to tell which file it was on.

Progress goes to stderr only; the written reports stay metrics-only, since real corpora are private invoices.

Verification

  • swift build, swift test — 177 tests, no regressions
  • swift format lint --strict — silent
  • Tools/EvalHarness builds
  • Mock accuracy with --invariant true and --invariant false: both exit 0, each report states its mode
  • Mock compare with arms differing only by invariant=: exits 0, both arm labels show the mode
  • Progress lines confirmed on stderr and absent from report files

EvalInvoice's sum(lineTotals)+tax≈grandTotal check rejects nearly every
extraction on weak models, leaving accuracy at 0.0% (0/0) with no scored
fields. Add invariant=true|false (default true) on RunConfig / compare specs
and --invariant for accuracy so A/B can measure the extractor rather than the
gate. Mode is always visible in report labels.

Also emit per-file progress to stderr in AccuracyRunner (index, total,
relative path, elapsed seconds) so long model runs are not mistaken for hangs.
Reports stay metrics-only.
@asaptf
asaptf merged commit 0213daf into main Aug 10, 2026
2 of 3 checks passed
@asaptf
asaptf deleted the eval/invariant-switch-and-progress branch August 10, 2026 10:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant