Fleet changelogs · dev.ecs0.net
rdmsm4x-changelog-20260831-1244-rdreceipt-parser-bakeoff-and-tip-slip-fix

rdmsm4x — RDReceipt parser bake-off and tip-slip total fix

2026-08-30 21:25 EDT → 2026-08-31 12:55 EDT (two sittings) on rdmsm4x. Claude Code, Opus 5.

One line: RDReceipt's two parsers were measured head-to-head on 37 real receipts, the app tree's parser won and is now v1's parser, and the systematic total bug that measurement exposed was fixed — charged-total accuracy went from 67.6% to 94.6%, which is what stands between the project and a correct expense report.

Scope

One project: ~/dev/apps/RDReceipt (worked in its worktree .claude/worktrees/rdreceipt-chat-thread-setup-a94cd2). No system settings, no installs, no other host touched. One fleet file edited: ~/dev/PROJECTS.md (the RDReceipt row).

What changed

File Change
tools/parser_bakeoff/ (new) SwiftPM tool + score.py; runs Packages/rdRECEIPT's parser over the app harness's OCR text so both parsers see byte-identical input
PARSER-BAKEOFF.md (new) the measurement report and verdict
Apps/ReceiptRoo/Tools/check_fixtures/FixtureCheck.swift (new) executes core/fixtures/rdRECEIPT_field_parsing.json against the real parser; exits non-zero on mismatch
Apps/ReceiptRoo/Shared/Engine/ReceiptFieldParser.swift decimal-comma money pattern; new tipSlipTotal; version bumped to receipt-field-parser-v2
core/fixtures/rdRECEIPT_field_parsing.json 7 → 9 cases; the two new ones pin the tip-slip and decimal-comma shapes
STATUS.md, ISSUES.md, SESSION-STATE.md, CLAUDE.md items 5 and 5a closed, findings filed, command reference extended
~/dev/PROJECTS.md RDReceipt row: evidence and next action

Commands run (the ones that produced the verdicts)

# measure the app parser over the real corpus
swiftc Tools/measure_classifier/Harness.swift Shared/Engine/*.swift Shared/Models/Receipt.swift \
  -o <scratch>/receiptMeasure -parse-as-library -lsqlite3
<scratch>/receiptMeasure ~/Documents/_receipts/expense_receipts_ALL <scratch>.sqlite3 <scratch>.json

# run the package parser over that same text, then score both
cd tools/parser_bakeoff && swift build && .build/debug/parser-bakeoff <scratch>.json <out>
./tools/parser_bakeoff/score.py <out>.json data/real-corpus/parser-bakeoff/ground-truth-20260830.json

# the fixture check, and the app build
swiftc Tools/check_fixtures/FixtureCheck.swift Shared/Engine/ReceiptFieldParser.swift -o <scratch>/fixtureCheck -parse-as-library
<scratch>/fixtureCheck ../../core/fixtures/rdRECEIPT_field_parsing.json
xcodebuild -project ReceiptRoo.xcodeproj -scheme ReceiptRoo-macOS -configuration Debug build

Evidence

Backups / recovery

Nothing deleted or overwritten. All work is on work/expense-export-and-parser-bakeoff at c5c3513, pushed to github.com/richhdoty/RDReceipt (private). main is untouched at 488dede. Scratch databases and measurement JSON live only in this session's scratch directory and carry real OCR text — they are not in the repo and .gitignore blocks their shapes.

Outstanding owner actions

  1. Merge the branch into main. Deliberately not done: the primary checkout has another session's uncommitted dev.dataroo.net → dev.ecs0.net rename in ISSUES.md and SESSION-STATE.md, the same two files this branch changes. Merge once that sweep is committed.
  2. Decide the package's optional-fields question before the storage swap (STATUS.md item 6): ExpenseReceipt publishes an unparsed date as today and an unparsed total as $0.00, and its schema is the one the SQLCipher vault carries.
  3. The SAP Concur recon is still blocked at sign-in, and the MCP privacy ruling is still open.

No secrets are recorded here.