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 buildEvidence
- Bake-off, 37 real receipts, identical input text:
app parser total 25/37 (67.6%) vs package 4/37 (10.8%); date offered
36/37 vs 16/37 (the package silently returned today 21 times);
merchant identical on 32/37, app 1–0 on the disagreements.
UnifiedReceiptParserreturned identical fields toHeuristicRegexParseron all 37. - After the fix, re-measured: totals 35/37 = 94.6%. Exactly 10 fields changed, all totals, all wrong→right — no merchant, date or currency moved and no already-correct total changed.
xcodebuild -scheme ReceiptRoo-macOS→** BUILD SUCCEEDED **.- Fixtures: 9 cases, 9 pass. The checker was made to
fail against a doctored expectation
(
FAIL … want 99.99, got 56.62, exit 1) before being trusted. - Push verified at the remote: 485 files, 0 from
data/real-corpus/, no ground-truth or measurement JSON, repoPRIVATE.
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
- Merge the branch into
main. Deliberately not done: the primary checkout has another session's uncommitteddev.dataroo.net→dev.ecs0.netrename inISSUES.mdandSESSION-STATE.md, the same two files this branch changes. Merge once that sweep is committed. - Decide the package's optional-fields question before the
storage swap (
STATUS.mditem 6):ExpenseReceiptpublishes an unparsed date as today and an unparsed total as $0.00, and its schema is the one the SQLCipher vault carries. - The SAP Concur recon is still blocked at sign-in, and the MCP privacy ruling is still open.
No secrets are recorded here.