Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 9 additions & 2 deletions .github/workflows/upgrade-docs.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Upgrade Documentation Dependencies
name: Upgrade Documentation Dependencies

on:
schedule:
Expand Down Expand Up @@ -83,11 +83,18 @@ jobs:
run: |
npm install -D mintlify@latest

# Only the scheduled and manual runs should open an upgrade PR. On
# pull_request and push events the checkout is a detached HEAD, which
# create-pull-request rejects without an explicit `base`; opening a PR
# as a side effect of someone else's docs edit is also not the intent.
- name: Create Pull Request
if: steps.check_updates.outputs.updates_available == 'true'
if: >-
steps.check_updates.outputs.updates_available == 'true' &&
(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
base: main
commit-message: 'chore: upgrade Mintlify CLI to ${{ steps.check_updates.outputs.latest_version }}'
title: 'chore: upgrade documentation dependencies'
body: |
Expand Down
119 changes: 63 additions & 56 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Overview

TaxFront is a full-stack tax document management platform with a React frontend and Python backend. It features user authentication, document management, tax form automation, and AI-powered agents for tax and audit tasks.
TaxFront is a tax document management platform: a React frontend talking to Firebase (Auth, Firestore, Storage) and TypeScript Cloud Functions. It features user authentication, document management, Gemini-based document extraction, and AI agents for tax and audit tasks.

**The Python `backend/` is superseded and unreachable from the running app.** `firebase.json` deploys only `functions/`; the frontend calls Firebase callables and never references `VITE_API_URL`, the variable the Flask service is exposed under in `docker-compose.yml`. Treat `backend/queue/`, `backend/embedding/`, `backend/src/`, `backend/tax_forms/`, `backend/parser/`, and `backend/agents/` as dead code — do not plan work around them without checking `TASKS.md` first.

## Common Commands

Expand All @@ -16,14 +18,17 @@ TaxFront is a full-stack tax document management platform with a React frontend
- **Generate API types**: `cd frontend && npm run generate:api` (from OpenAPI schema at `../api-docs/openapi.yml`)
- **Preview build**: `cd frontend && npm run preview`

### Backend Development
### Cloud Functions (`/functions`) — the live backend
- **Typecheck**: `cd functions && npx tsc --noEmit`
- **Build**: `cd functions && npm run build`
- **Run emulator**: `cd functions && npm run serve`
- **Deploy**: `cd functions && npm run deploy`
- **Run tests**: `cd functions && npm test` or `npm run test:watch` for watch mode

### Legacy Python backend (`/backend`) — not deployed
Commands kept for archaeology only; nothing here runs in production.
- **Run Flask app**: `cd backend && python app.py`
- **Run Firebase emulator**: `cd backend/parser/functions && firebase emulators:start`
- **Run backend tests**: `cd backend && python -m pytest` or `python -m pytest --cov` for coverage
- **Run parser function tests**: `cd backend/parser/functions && python -m pytest test_functions.py`
- **Run agent tests**: `cd backend/agents && python -m pytest tests/`
- **Code formatting**: `cd backend && black .` (configured in requirements)
- **Linting**: `cd backend && flake8`
- **Run backend tests**: `cd backend && python -m pytest`

## Project Structure

Expand All @@ -43,37 +48,39 @@ TaxFront is a full-stack tax document management platform with a React frontend
- **Testing**: Vitest with jsdom environment
- **State management**: React hooks with Firebase hooks (`react-firebase-hooks`)

### Backend (`/backend`)
- **Main app**: `app.py` - Flask application entry point
- **Core components**:
- `/agents/` - LangChain-based AI agents for accounting and auditing tasks
- `accountant_agent.py` - Tax accounting assistant
- `auditor_agent.py` - Audit verification
- `base_agent.py` - Agent base class
- `tools/` - Tool definitions for agents (tax_tools.py, document_tools.py)
- `/parser/functions/` - Firebase Cloud Functions (Python)
- `main.py` - Function handlers
- `parser.py` - Document parsing logic
- `/tax_forms/` - Tax form automation and filling
- `form_definitions.py` - Form schema definitions
- `form_filler.py` - Browser-based PDF form filling
- `routes.py` - API endpoints
- `/embedding/` - Vector embedding and RAG pipeline
- `/queue/` - Async task processing
- `task_manager.py` - Task orchestration
- `task_processors.py` - Task execution
- `/src/` - Additional utilities and RAG pipelines
- `/utils/browser.py` - Browser automation utilities
- **Dependencies**: LangChain, Firebase Admin SDK, PyPDF2, Pillow, Pytest
- **Database**: Cloud Firestore
- **File storage**: Firebase Storage
### Cloud Functions (`/functions`)
- **Built with**: TypeScript, Genkit, `@genkit-ai/googleai`, Firebase Admin SDK
- **Entry point**: `src/index.ts` — all callables and triggers
- `runAccountant` / `runAuditor` - agent entry points
- `getTaxDocuments` / `getTaxSummary` / `createUserProfile`
- `processNewTaxDocument` - Firestore `onDocumentCreated` trigger
- **`src/flows/`** - agent and pipeline definitions
- `extractor.ts` - Gemini document extraction. Its `EXTRACTION_PROMPT` is the authoritative list of extracted field names per form type.
- `accountant.ts` / `auditor.ts` - agent flows
- **`src/tools/`** - Genkit tool definitions the agents call
- `accountantTools.ts` - `build_tax_summary`, `suggest_deductions`
- `auditTools.ts` - `check_audit_triggers`, `cross_reference_income`, `calculate_audit_risk_score`
- `taxCalc.ts` - 2024 brackets, standard deductions, SS wage base
- `documentTools.ts` - Firestore document fetching
- **`src/semantic/`** - see below
- **Database**: Cloud Firestore. **File storage**: Firebase Storage.

### Semantic layer (`functions/src/semantic/taxFields.ts`)
Single source of truth for document classification, extracted-field aliases, and income aggregation. **Both tool sets must read from it.**

Before adding a field lookup or an income total to any tool, check whether it belongs here instead. The accountant and auditor previously kept parallel alias chains and disagreed on total income by a wide margin on the same documents; that class of bug is what this module exists to prevent.

- `safeFloat` / `pickAmount` - money parsing that tolerates `"$1,234.56"`. Never use bare `parseFloat` on extracted values.
- `classifyDocument` / `normalizeDocType` - the only correct way to branch on `documentType`; handles `1099-INT`, `1099 INT`, `1099int` alike.
- `FIELD_ALIASES` - canonical alias chains, most specific name first.
- `collectIncome` - the authoritative aggregate. `totalIncome` is the one income figure; do not re-derive it.
- `documentIncome` - per-document income for single-document audit checks.

## Architecture Notes

### Frontend-Backend Communication
- Frontend uses Firebase SDK directly for auth and storage
- Backend Flask app handles complex processing (document parsing, form filling, AI agents)
- Firebase Cloud Functions serve as serverless workers
- Frontend uses the Firebase SDK directly for auth and storage
- All server work runs in TypeScript Cloud Functions, invoked via `httpsCallable` from `frontend/src/services/api.ts`

### Role-Based Access
- **Users**: Can upload documents, view dashboard, access tax calculator
Expand All @@ -88,41 +95,41 @@ TaxFront is a full-stack tax document management platform with a React frontend
5. Agents can process documents for tax/audit purposes

### Tax Form Automation
- Forms defined in `form_definitions.py` (including 1040, W-2, etc.)
- `form_filler.py` handles browser-based PDF form filling with proper field positioning
- Supports multi-page forms and custom field validation rules
- Uses Chromium browser automation
Not currently implemented. The Python `form_filler.py` (Chromium automation with manual field positioning) is dead code; if this is rebuilt, do it in the TS stack with `pdf-lib` AcroForm filling. See `TASKS.md`.

## Testing Strategy

- **Frontend**: Vitest for unit tests, jsdom environment for DOM testing
- **Backend**: Pytest for unit/integration tests
- Agent tests: `backend/agents/tests/`
- Function tests: `backend/parser/functions/test_*.py`
- **Coverage**: Use `pytest --cov` and `@vitest/coverage-v8`
- **Cloud Functions**: Vitest, in `functions/test/`. `cd functions && npm test` (or `npm run test:watch`).
- `taxFields.test.ts` covers the semantic layer; `tools.test.ts` invokes the Genkit tools directly.
- Tools are testable offline because their implementations are pure — they never reach the model. `vitest.config.mts` supplies a dummy API key so the Genkit `googleAI` plugin can construct.
- Keep the cross-tool agreement test in `tools.test.ts`: the accountant and auditor must report the same total income for the same documents. That invariant is the reason the semantic layer exists.
- **Coverage**: `@vitest/coverage-v8`

## Architecture Decisions

- `docs/open-policy-agent.md` — OPA/Rego for the audit trigger rules. Evaluated, not adopted; records what would change the answer.

## Development Workflow

1. Frontend changes: Make changes in `/frontend`, test with `npm test:watch`, dev server reflects changes instantly
2. Backend changes: Modify Python files, test with `pytest`, use Firebase emulator for Cloud Functions
3. New API endpoints:
- Add endpoint in backend
- Update OpenAPI spec at `api-docs/openapi.yml`
- Run `npm run generate:api` in frontend to sync types
4. Agent tools: Add to appropriate tool file in `/agents/tools/`, ensure tests in `tests/` directory
1. Frontend changes: Make changes in `/frontend`, test with `npm run test:watch`, dev server reflects changes instantly
2. Cloud Function changes: Edit `/functions/src`, `npx tsc --noEmit` to typecheck, `npm run serve` for the emulator
3. New API endpoints:
- Add the callable to `functions/src/index.ts`
- Add the client wrapper to `frontend/src/services/api.ts`
- Update the OpenAPI spec at `api-docs/openapi.yml`, then `npm run generate:api` in frontend to sync types
4. Agent tools: Add to the appropriate file in `functions/src/tools/`. Take field lookups and income totals from `src/semantic/taxFields.ts` rather than re-deriving them.

## Deployment

- **Frontend**: Automatically deployed via GitHub Actions workflow
- **Backend**: Cloud Functions deployed automatically
- **Docker**: Both services containerized (see docker-compose.yml)
- Frontend: Node.js build + Nginx serving
- Backend: Python Flask + Gunicorn
- **Frontend**: Firebase Hosting, deployed via GitHub Actions
- **Cloud Functions**: `firebase deploy --only functions`
- **Docker**: `docker-compose.yml` still builds the legacy Flask service; it is not part of the deployed system.

## Key Dependencies & Versions

- **Frontend**: React 18.3, Vite 6.4, TailwindCSS 3.4, TypeScript 5.7
- **Backend**: Python 3.12+, Flask, LangChain, Firebase Admin SDK, PyPDF2
- **Cloud Functions**: Node 22, TypeScript 5.6, Genkit 1.34, `@genkit-ai/googleai` 1.28, firebase-admin 13

## AI Model Standards
- **Mandatory Model**: AI agents (Accountant and Auditor) MUST use `googleai/gemini-2.5-flash`.
Expand Down
54 changes: 36 additions & 18 deletions TASKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,43 @@
- Add `api.runFullAnalysis()` to `frontend/src/services/api.ts`
- Add a single "Analyze My Taxes" button to `Dashboard.tsx` that calls it and renders both sections

### Document parsing pipeline (Cloud Functions)
The old Python `parser.py` handled PDF OCR and data extraction. The current `processNewTaxDocument` trigger (`functions/src/index.ts`) only stamps a timestamp and sets `status: "processed"` — it does not extract any data. Until this is rebuilt, `extractedData` is always empty and agents cannot do real analysis.
### Decide the fate of the Python `backend/`
`firebase.json` deploys only `functions/` (TypeScript). The frontend talks exclusively to Firebase callables — nothing in `frontend/src` references `VITE_API_URL`, the variable the Flask service is exposed under in `docker-compose.yml`. `backend/app.py` does not import `task_manager`, `tax_forms`, or `form_filler` either.

**Approach:** Replace the stub with a real extraction pipeline in TypeScript Cloud Functions.
So these are unreachable from the running app: `backend/queue/`, `backend/embedding/`, `backend/src/*rag_pipeline.py`, `backend/tax_forms/`, `backend/parser/`, `backend/agents/`.

**Implementation notes:**
- Use the Google Cloud Document AI API (`@google-cloud/documentai`) or the Gemini file API (`ai.generate` with inline PDF) to extract structured fields from uploaded PDFs
- Map extracted fields to the schema expected by `build_tax_summary` and `check_audit_triggers`:
- W-2: `wages`, `federal_tax_withheld`, `state_tax_withheld`, `social_security_tax_withheld`, `medicare_tax_withheld`, `employer_ein`
- 1099-NEC: `nonemployee_compensation`
- 1099-INT: `interest_income`
- 1099-DIV: `total_dividends`, `ordinary_dividends`
- 1098: `mortgage_interest`
- Write extracted fields to `taxDocuments/{id}.extractedData` in Firestore
- Set `status: "processed"` on success, `status: "error"` with `errorMessage` on failure
- The document is already in Firebase Storage when the trigger fires — fetch it via the Admin SDK (`getStorage().bucket().file(data.storagePath).download()`)
- Consider a `documentType` auto-detection step before extraction (infer from filename or first-pass OCR)

**Testing without the pipeline:** Manually write `extractedData` directly to a Firestore document to test agents end-to-end.
**Decision needed:** delete them, or keep them and mark them clearly as superseded. Right now they read as live architecture and have already caused at least one round of misdirected planning. If tax form filling gets rebuilt, do it in the TS stack with `pdf-lib` rather than reviving `form_filler.py`'s browser automation.

### Externalize audit thresholds
`check_audit_triggers` hardcodes its rule constants: `> $1M` / `> $500K` income tiers, `0.9` / `0.75` Schedule C expense ratios, `0.2` charitable-to-income ratio, `$1,500` Schedule B floor. They are invisible to the agent except through the tool's prose description, and a CPA cannot review or adjust them without a code change.

Candidate approaches: plain exported constants in `src/semantic/`, an Apache Ossie YAML semantic model (`ai_context` would also ground the agents), or Rego policies evaluated via `@open-policy-agent/opa-wasm`.

The OPA option is written up in `docs/open-policy-agent.md` — evaluated and deliberately not adopted, with the two conditions that would change the answer (rules branching by tax year / state / filing status, or a CPA rather than an engineer owning them).

## Done

### Test suite for `functions/`
Vitest, in `functions/test/`. `npm test` / `npm run test:watch`. 57 tests, offline — the Genkit tools are invoked directly and their implementations never reach the model, so a dummy key in `vitest.config.mts` is enough.

- `taxFields.test.ts` — the semantic layer: money parsing, alias resolution, document classification, income aggregation.
- `tools.test.ts` — the tools themselves, including the cross-tool agreement invariant (accountant and auditor must report the same total income) and a named regression case for each bug listed below.

Verified by mutation: reintroducing the two original bugs in `taxFields.ts` fails 22 of the 57 tests.

### Unify tax semantics across the accountant and auditor tools
`accountantTools.ts` and `auditTools.ts` each carried their own field-alias chains, document-type matching, and income totals, and they disagreed.

Both now read from `functions/src/semantic/taxFields.ts`. See "Semantic layer" in `CLAUDE.md`.

Fixed along the way:
- `auditTools` parsed money with bare `parseFloat`, so `"$120,000.00"` became `0` and `"1,234.56"` became `1`. Now uses the shared `safeFloat`.
- `check_audit_triggers` read income as `income ?? gross_wages ?? total_income`, but the extractor writes `wages` for a W-2 — the high-income triggers never fired on a W-2. Now resolves per document category.
- `cross_reference_income` omitted interest, dividends, and Schedule C income from `totalDocumentedIncome`. On a mixed seven-document set the accountant reported $196,000 and the auditor $65,000; both now report $196,000.
- The auditor's `1099-INT` / `1099-DIV` matching did not strip separators, so `"1099 INT"` was silently skipped.

### Document parsing pipeline (Cloud Functions)
Shipped as `functions/src/flows/extractor.ts` — downloads the file, sends it inline to `gemini-2.5-flash`, and writes structured fields to `taxDocuments/{id}.extractedData`, with `status: "error"` and `errorMessage` on failure. The prompt in that file is the authoritative list of extracted field names per form type.

### Wire filing status from user profile
Currently hardcoded as `'single'` in `Dashboard.tsx` line 48. Should be pulled from the authenticated user's Firestore profile instead.
`Dashboard.tsx` reads `filingStatus` from the user's Firestore profile and surfaces a `filingStatusMissing` prompt when it is absent. The `useState('single')` on line 41 is only an initial value.
Loading
Loading