rdmsm4x-changelog-20260830-1255-rdreceipt-date-range-csv-xlsx-export
Built RDReceipt's date-range CSV/XLSX expense export (STATUS.md item 4), the first feature toward turning a date range into a Concur-ready file.
- When: 2026-08-30 12:41–12:56 EDT
- Host: rdmsm4x
- Project:
~/dev/apps/RDReceipt(worktree.claude/worktrees/rdreceipt-chat-thread-setup-a94cd2, detached HEAD) - Commit:
aa942a2(parentaa0875c)
Scope
One project, one repo. No fleet-wide, system, or configuration changes. No packages installed. No secrets read or written. Not pushed — the worktree is intentionally on a detached HEAD.
Files added
| File | Purpose |
|---|---|
Packages/rdRECEIPT/Sources/rdRECEIPTCore/Models/ExportDateRange.swift |
Date range with the inclusive-end rule; range →
VaultFilter; YYYY-MM-DD parsing |
Packages/rdRECEIPT/Sources/rdRECEIPTUI/Export/XLSXExportGenerator.swift |
Real .xlsx workbook, typed date/number cells, 4
layouts, totals row |
Packages/rdRECEIPT/Sources/rdRECEIPTUI/Export/ZipArchiveWriter.swift |
Minimal stored-method ZIP container + CRC-32, so no dependency is needed |
Packages/rdRECEIPT/Tests/rdRECEIPTCoreTests/ExportDateRangeTests.swift |
12 tests |
Packages/rdRECEIPT/Tests/rdRECEIPTUITests/XLSXExportGeneratorTests.swift |
17 tests, incl. an independent ZIP reader |
Files modified
Packages/rdRECEIPT/Sources/rdRECEIPTUI/Export/CSVExportGenerator.swift— addeddateRange; made the previously deadfilterfield actually apply.Packages/rdRECEIPT/Sources/rdRECEIPTCLI/main.swift—exportgained--from,--to,--layout, andxlsx; help text updated to match the parser.CLAUDE.md,STATUS.md,SESSION-STATE.md,ISSUES.md— status, evidence, corrections.
Verification evidence
swift build→Build complete!swift test→ 534 tests / 89 suites / exit 0, zero failures (baseline 505/87; +12 Core, +17 UI)unzip -ton generated workbooks →No errors detected- openpyxl with
warnings.simplefilter("error")→ loads clean; dates return asdatetime, amounts as numbers; number formatsyyyy\-mm\-ddand#,##0.00; totals row summed 606.15 correctly qlmanage -t→ macOS Quick Look produced a thumbnail (native importer parsed it)- CLI exercised against the live vault (19 receipts): full export, narrow range, single day, empty range, layout switch, and four error paths (bad date, inverted range, bad layout, bad format) all behaved correctly and exited non-zero where they should.
Strongest single piece of evidence: a single-day
export for 2026-08-30 returned its 3 receipts, whose timestamps are
16:43–16:51. A naive midnight end-bound would have returned zero — this
is the silent wrong-report failure the whole
ExportDateRange type exists to prevent, proven against live
SQL rather than only in unit tests.
Corrections made to previously recorded facts
The per-target test-count split was wrong in three
files. CLAUDE.md, STATUS.md and
SESSION-STATE.md all published
Core 51 · OCR 66 · Parser 54 · Vault 55 · E2E 248 · UI 31.
The total (505) was always correct, but the per-target
attribution was read off swift test's six summary lines in
printed order — which is completion order, not target
order, because the targets run in parallel. True baseline:
Core 31 · OCR 55 · Parser 54 · Vault 51 · E2E 248 · UI 66.
Corrected by six swift test --filter <target> runs
cross-checked against @Test declaration counts per
directory. It survived a week because the total reconciled.
Data handling
Test exports were written to a mktemp -d directory with
mode 700 (not a world-readable temp path) because they derived from the
encrypted vault, and were deleted afterwards along with the sample
workbook/CSV. Only aggregate counts and times-of-day were printed — no
merchant names, amounts, or notes. .gitignore re-checked:
/data/real-corpus/ and *measure*.json still
excluded.
Outstanding owner actions
- The commit sits on a detached HEAD and is on no
branch. It is reachable via
aa942a2and the reflog, but nothing points at it. Create a branch or tag if it should be kept. - Concur field recon is still blocked at sign-in (three Chrome browsers connected, none selected). The export's column schema is generic until that lands — a deliberate, cheap-to-change choice, not a defect.
- CSV money formatting drops trailing zeros
(
412.00→412). Left alone deliberately: it is pre-existing across all four layouts and thelegacyOraclelayout is tied to E2E oracle validation, wherecore/CONTRACT.mdsays the fixture wins until a human rules. Fix with the Concur reshape and re-run the fixtures.
How to undo
git -C ~/dev/apps/RDReceipt/.claude/worktrees/rdreceipt-chat-thread-setup-a94cd2 reset --hard aa0875c
restores the pre-session state. The five added files are untracked by
that commit and would need removing separately. No state outside the
repo was changed.