Finance Reports

A personal project in financial data, automation, and AI review

How I designed and coordinated a weekly expense-report workflow with explicit checks and recovery paths.

I built Finance Reports to turn my own expense records into a weekly review I can act on. The useful part is not just an email with a spending total. It is knowing what has been recorded, what still needs attention, and whether the explanation agrees with the numbers.

This is a personal-use automation, not a service visitors can connect their accounts to. This page describes the project; it does not display my ledger or run the reporting workflow. The Finance Reports privacy policy explains its Google access and how its private data moves through the system.

NoteStatus: core implemented, live rollout pending

As of September 21, 2026, the core reporting and revision code has passed CI. Subscription authentication and the fallback path are implemented locally and have passed synthetic tests. Google authorization, hosted end-to-end checks, and the first delivered report are still pending. Scheduled delivery remains disabled. This is a record of the design and work so far, not a claim of proven production reliability.

The problem I wanted to solve

Capturing expenses and reviewing them are different jobs. A quick entry from my phone may still be waiting for my PC to ingest it. A day with no recorded transactions might mean I spent nothing, or that I have not entered the receipts yet. A report that silently treats either situation as complete would give me confidence I had not earned.

I specified two checkpoints: a Sunday morning reminder to catch omissions, followed by a Monday report for the completed week. The reminder shows daily totals, uncategorized entries, possible duplicates, and pending captures. The report adds category and needs/wants totals, the largest expenses, a prior-week comparison, and changes since the reminder.

How the workflow fits together

  1. I record expenses through the existing capture process. My PC ingests them into the ledger in Google Drive; cloud reporting does not replace that ingestion step.
  2. A GitHub Actions runner downloads a consistent snapshot of the ledger, reference files, and pending captures. It checks that the required files and schemas are present.
  3. Python and Polars calculate the report facts. Pending captures stay separate from totals, and days without entries are labeled as having no expenses recorded.
  4. Codex drafts observations from those facts. Claude reviews the draft against the same evidence, with one bounded opportunity to revise and review again.
  5. Gmail sends the report to my configured address. Private snapshots and delivery records in Drive support later corrections and help prevent duplicate sends.

The Sunday reminder uses a template. The Monday report uses AI for interpretation; Python owns the arithmetic. If one provider is unavailable, the other can draft and review in separate calls, with a prominent notice that cross-provider review was unavailable. That fallback is not equivalent to independent review. Failed review does not produce an unchecked report.

My role and the coordination behind it

I defined the requirements, chose the reporting cadence, and made the tradeoffs about privacy, cost, and how much manual setup was reasonable. I used Codex and Claude as implementation and review assistants, then turned their findings into prioritized fixes and acceptance checks.

That coordination included keeping unrelated repository changes separate, working through review findings in tiers, and reserving architectural decisions for explicit discussion. The work spans two repositories: a private automation repository and this public website. GitHub issues and a pull request track the implementation, while setup instructions make the account authorization and deployment steps repeatable.

I also changed the authentication plan to use my existing subscriptions, with no automatic API-billing fallback. That required more than swapping a credential: the workflow needs an isolated login, secure persistence of refreshed credentials, serialized execution, and a documented recovery process. Real account access and usage still need live verification.

Decisions that made this more than a scheduled prompt

  • Data completeness: missing files are errors, not empty ledgers. A source that changes during download is retried once, then rejected if a consistent snapshot cannot be obtained.
  • Reconciled totals: daily and category sums must agree with the headline total, including uncategorized spending. Positive credits are shown separately rather than assumed to be refunds.
  • Traceable corrections: a revised report uses a new snapshot and identifies ledger changes against the preceding confirmed report. Earlier reports retain their delivery history.
  • Ambiguous delivery: a timeout after sending can mean the email was accepted. The workflow records send intent and requires reconciliation instead of blindly retrying an uncertain send.
  • Credential separation: Google permissions, email delivery, and model execution are separate. Each model process receives its own credentials and bounded evidence, not the Google tokens.
  • Operational limits: deadlines, invocation limits, sanitized failures, and a disabled-by-default schedule make the automation controllable when an external service fails.

Validation and what remains

Synthetic tests cover period boundaries, spending classification, missing and changing source files, duplicate-send prevention, report revisions, model review failures, and credential-state handling. The implementation handoff on September 21, 2026 recorded a local full-suite result of 785 passing tests and two skips, with formatting and lint checks passing. That historical count covers the private repository as a whole, not just this feature or this website.

Before sending live data to the models, I still need to verify training opt-out and applicable data-use terms for both authenticated provider accounts, including the fallback path. Until then, model checks must use synthetic data only. This is a manual setup requirement; the reporting code does not check those account settings.

Before enabling the schedule, I also need to verify both model providers on the hosted runner, test Google permissions and report-folder operations, review a real-data dry run, confirm a diagnostic email reaches the inbox, and exercise the phone-to-PC capture path. I plan to review both emails during the first two weeks rather than assume a scheduled run means successful delivery.

There are deliberate limits: GitHub schedules can run late, capture ingestion still depends on my PC, and a model review can miss an error. I have not measured time saved or long-term reliability. The project demonstrates the process of turning a useful idea into a testable workflow, including the failure and recovery cases that are easy to leave out of a demo.

Why the Google authorization pages live here

Google’s production OAuth setup asks for an app homepage and a privacy-policy link. This case study gives that homepage a useful purpose: it explains what I built and why the permissions exist. Publishing these pages does not publish the private source repository, financial records, reports, or credentials.

Google’s personal-use verification exception does not resolve the branding fields required by my setup. The remaining configuration must also meet Google’s branding and domain requirements; publishing these pages alone does not establish Google approval.

Read the Finance Reports privacy policy.