Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f3b23e6
feat(currency): narrow currency core, real-statement PDF fixes, and F…
aafre Aug 30, 2026
1bea0b3
fix(test): isolate CLI fx tests from process-wide settings cache
aafre Aug 30, 2026
2fe9562
feat(imports): bind statements to typed accounts
aafre Aug 30, 2026
12db20f
fix(analytics): calculate cash from liquid account signs
aafre Aug 30, 2026
2a49b5f
feat(transfers): persist auditable transfer matches
aafre Aug 30, 2026
2dfd09c
feat(imports): add safe idempotent undo
aafre Aug 30, 2026
2c20957
feat(imports): expose reconciliation blocking codes
aafre Aug 30, 2026
cb44fda
feat(imports): expose review, reconciliation, and transfer workflows
aafre Aug 30, 2026
56d6720
test(accounts): cover canonical signs and cash coverage
aafre Aug 30, 2026
b943295
fix(imports): preserve receipt totals and unlink suppression
aafre Aug 30, 2026
d52a1de
feat(imports): add strict HDFC delimited adapter
aafre Aug 30, 2026
94a6e53
feat(imports): persist HDFC metadata and reconciliation
aafre Aug 30, 2026
8549743
test(imports): cover HDFC delimited workflow
aafre Aug 30, 2026
5a997e0
feat(web): add HDFC account confirmation flow
aafre Aug 30, 2026
1aa658c
fix(imports): accept blank HDFC amount sides
aafre Aug 30, 2026
0cbd0df
docs(imports): document HDFC delimited support
aafre Aug 30, 2026
3a2595b
fix(imports): report HDFC reconciliation source rows
aafre Aug 30, 2026
719eebb
feat(imports): HDFC delimited import, PDF extraction, and reconciliat…
aafre Sep 5, 2026
bade076
fix(web): make the dashboard usable on first load
aafre Sep 5, 2026
015d815
chore: green the quality gate (ruff, mypy) on the HDFC WIP
aafre Sep 5, 2026
dbf4d09
feat(imports,web): fix manual re-categorisation and ambiguous-sign co…
aafre Sep 5, 2026
7c4c112
docs: handoff note for post-v0.2 open issues
aafre Sep 5, 2026
3eab069
perf(api,web): bound transactions query by date, add /transactions/mo…
aafre Sep 6, 2026
3b31880
docs: test handoff for PR #21
aafre Sep 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,8 @@ __marimo__/

# Streamlit
.streamlit/secrets.toml

# Local scratch / one-off runners / db backups
scratch/
*.db.bak
/main.py
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
59 changes: 59 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

Everything runs through `uv` (never bare `python`/`pytest`):

```bash
uv sync # install locked deps
uv run pfa db migrate # required before any CLI/API workflow
uv run pfa <cmd> # CLI entrypoint (pfa.cli.app:app)
uv run uvicorn pfa.api.app:app --host 127.0.0.1 --port 8000
uv run pfa eval-classifier # classifier smoke, needs a running Ollama model
```

Full quality gate — all four must pass before claiming work is complete (matches CI):

```bash
uv run ruff check . && uv run ruff format --check . && uv run mypy src && uv run pytest
```

mypy runs in `strict` mode over `src` only. Single test: `uv run pytest -k 'test_name'`.

## Hard rules

- **The LLM never computes financial facts.** Every number a user sees must come from deterministic
Python/SQL in `analytics`, `planning`, or `domain`, surfaced through a typed read-only tool. The
agent interprets and explains; it does not calculate. Adding a number to a prompt/response that no
tool returned is a bug.
- **Local models only.** No cloud/hosted LLM APIs, no telemetry. Ollama at `PFA_OLLAMA_BASE_URL` is
the only inference path. Live AI paths are fine to exercise, but `uv run pytest` must stay offline —
the normal suite never calls Ollama.
- **Money is integer minor units.** Use `domain.money.Money` and `Decimal`; binary floats must never
reach a monetary calculation. v0.1 is GBP-only and rejects other currencies rather than summing them.
- **Schema changes go through Alembic** (`alembic/versions/`). Runtime service code never creates or
upgrades tables.

## Domain semantics

Getting these wrong silently corrupts reported figures:

- Owned-account transfers are persisted for audit but excluded from both income and spending.
For paired transfers, only the debit/outgoing side contributes to a metric.
- `savings rate = (saving transfers + investment transfers) / income` for the period.
- Spending = classified expenses + fees − refunds. A refund reduces spending in the month it posts,
even if the purchase was earlier.
- Cash withdrawals move bank cash to physical cash: total tracked cash is unchanged, and the amount
is not spending until the underlying purchase is classified.

Layer boundaries and deliberate constraints: @docs/architecture.md
Agent design, tool contracts, and grounding rules: @docs/ai-engineering.md

## Repo etiquette

- Work on a feature branch and open a PR; never commit directly to `main`.
- Conventional commits: `type(scope): subject` (e.g. `fix(analytics): ...`). The `F-NN` IDs in older
commits refer to a completed validation pass — don't invent new ones.
- `.env` is local and gitignored; copy `.env.example` and use `PFA_`-prefixed settings.
66 changes: 66 additions & 0 deletions PRODUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Product

<!-- impeccable:product-schema 1 -->

## Platform

web

## Stack

delegated: plain HTML/CSS/vanilla JavaScript served by the existing FastAPI app; no frontend framework added for a local-first dashboard

## Users

One person reviewing their own private financial activity on a local machine.

## Product Purpose

PFA helps a person understand and improve their financial life over time through evidence-backed
analysis, deterministic calculations, scenarios, and concise explanations.

## Positioning

Financial facts come from deterministic Python/SQLite services. The local model interprets those
facts but is never the source of totals, balances, rates, or projections.

## Operating Context

The user imports synthetic or personal bank CSVs, reviews monthly changes, checks budgets and goals,
asks natural-language questions, and simulates decisions. The application runs locally with SQLite
and optional Ollama.

## Capabilities and Constraints

Transactions, accounts, budgets, goals, imports, analytics, scenarios, recurring-payment evidence,
anomaly/trend signals, a CLI, and a FastAPI API are implemented. Initial behavior is read-only and
advisory. No cloud APIs, money movement, brokerage execution, or hosted telemetry.

## Brand Commitments

The product name is PFA (Personal Finance Agent). Voice is concise, plain-language, evidence-backed,
transparent about assumptions, and respectful of the user's decision authority.

## Evidence on Hand

`data/demo_transactions.csv` is synthetic demonstration data covering June–August 2026. No real
financial claims, testimonials, or commercial proof should be fabricated.

## Product Principles

- Deterministic calculations outrank model guesses.
- Local privacy is the default.
- Show evidence and assumptions beside recommendations.
- Keep the user in control; initial features are read-only.
- Prefer small, explainable workflows over speculative infrastructure.

## Accessibility & Inclusion

The dashboard must support keyboard navigation, visible focus, semantic HTML, responsive layouts,
adequate contrast, reduced-motion preferences, and text alternatives for status indicators.

## Assumptions

The UI surface, visual direction, and frontend implementation path are inferred because no visual
brief or question mechanism was available in this session. Revisit these if the user supplies a
different dashboard scope, brand direction, or frontend constraint.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ flowchart LR

- **Local-first ledger** — SQLite is the source of truth; no hosted telemetry or cloud model requirement is implemented.
- **Deterministic finance engine** — income, spending, savings rate, budgets, goals, recurring evidence, anomalies, trends, and scenarios are calculated in Python from integer minor units.
- **Statement import studio** — upload CSV or PDF statements in the browser, preview extracted rows,
- **Statement import studio** — upload CSV, HDFC India Delimited `.txt`, or PDF statements in the browser, preview extracted rows,
review warnings/errors, exclude rows, state the statement's sign convention, and commit explicitly.
Commit is blocked while any included row has a blocking error or the sign convention is unanswered.
- **PDF + OCR path** — digital PDFs use `pdfplumber`; scanned PDF pages can fall back to local Tesseract OCR when installed.
Expand Down Expand Up @@ -131,13 +131,13 @@ PFA is strict about money because small mistakes corrupt advice:
| Path | Supported now | Notes |
| --- | --- | --- |
| CLI | Local UTF-8 CSV files | `uv run pfa import <path.csv>`; `--dry-run` validates without persistence. |
| Browser/API preview | CSV and PDF uploads | `POST /imports/preview` stages a bounded local upload, extracts candidates, and deletes raw uploaded bytes after extraction. |
| Browser/API preview | CSV, HDFC Delimited `.txt`, and PDF uploads | `POST /imports/preview` stages a bounded local upload, extracts candidates, and deletes raw uploaded bytes after extraction. |
| Digital PDF | Yes, best-effort | Uses `pdfplumber` table/word extraction with source-page provenance. |
| Scanned PDF | Basic local OCR fallback | Requires Tesseract installed on `PATH`; OCR-derived rows carry review warnings, and low-confidence date/amount fields block commit. |

Upload limits default to 15 MiB, 100 PDF pages, 10,000 candidate rows, and a 24-hour TTL for uncommitted normalized batches. Committed batches keep metadata and transaction IDs, not raw statement bytes.

CSV imports accept common aliases for date, description, amount, account, and transaction ID. They support signed `amount` columns, debit/credit columns, comma/semicolon/tab delimiters, UTF-8 BOM, row-level errors, duplicate detection, and manual review for unresolved classifications.
CSV imports accept common aliases for date, description, amount, account, and transaction ID. They support signed `amount` columns, debit/credit columns, comma/semicolon/tab delimiters, UTF-8 BOM, row-level errors, duplicate detection, and manual review for unresolved classifications. HDFC India Delimited exports are content-detected from their exact seven-column header, require a confirmed INR Current/Savings account, and reconcile ordered closing balances before commit.

For unsigned credit-card-style exports, the preview API supports an explicit `amount_sign` patch (`as_written` or `debit_positive`) so PFA does not silently guess whether positive values are purchases or credits.

Expand Down
Loading