The application
The frontend
Stand up the Vite React interface, wire upload to processing, and run both halves with one dev script.
By the end of this stage the app has a face. A React single-page app uploads a document, kicks off the pipeline through the API, and shows the result, and one script runs both halves of the project in a single terminal.
daveebbelaar/invoice-review1:42:38The stack
The frontend/ folder starts as an empty src plus an AGENTS.md describing the preferred setup, which is how Dave steers the coding agent instead of scaffolding by hand. The stack is Vite as the build tool and dev server, React with strict TypeScript, and Tailwind CSS for styling. Install and run:
cd frontend
pnpm install
pnpm run dev
# VITE ready in 312 ms
# Local: http://localhost:5173/The scaffold prompt points the agent at the API layer for the endpoints it needs and at the solution branch for the visual style, with an explicit instruction to base the logic on the current development branch. Reusing your own earlier work as a reference is a normal way to work with agents; copying its architecture wholesale is not.
One command for both
Two processes get old fast. The fix is a small script, and its size is the story. The agent's first version was over a hundred lines of environment checks; Dave's response was to ask it to write it shorter, and the keeper fits on a screen:
set -euo pipefail
cd "$(dirname "$0")/.."
(
cd backend
uv run --locked --no-sync uvicorn app.main:create_app --factory --reload
) &
(
cd frontend
pnpm dev
) &
trap 'kill 0' EXIT INT TERM
waitBoth processes start in the background, and the trap kills the pair when you exit. ./scripts/dev.sh is how you run the project from here on.
How the halves talk
The frontend holds no business logic. "The logic lives in Python." Every button click becomes an API call through one typed client:
const configuredBaseUrl = import.meta.env.VITE_API_BASE_URL
// Same-origin container builds set VITE_API_BASE_URL=/ so fetches stay relative.
export const apiBaseUrl = configuredBaseUrl === '/' ? '' : configuredBaseUrl.replace(/\/$/, '')frontend/src/lib/api.ts wraps every endpoint from the previous stage in a typed function, with the response shapes mirrored as TypeScript types in lib/types.ts. During local dev VITE_API_BASE_URL is http://localhost:8000, and the backend's CORS middleware already allows http://localhost:5173, the default allowed_origin in backend/app/config.py. That comment about / meaning same-origin becomes important at deployment.
The components map onto the flow Maya walks through, all under frontend/src/components/. WelcomePortal handles the landing screen, UploadStep does drag-and-drop with a preview, ProcessingStep covers the wait while the pipeline runs, and DocumentInbox lists the history.
Stay in the loop
Upload a sample, hit Process, and the first real result renders, classification with reasoning, extraction highlights, validation results, and the GL suggestion. The wiring is exactly the playground run_pipeline flow, now triggered by a button.
This is also where Dave issues his standing warning about agent-built apps: "you can very quickly get to a point of no return where I just don't know how this application works." His countermeasure is to make the agent document as it goes, which is where the repo's docs/api-and-pipeline.md comes from, and to keep interrogating the wiring until the mental model is solid. The docs folder is part of the product.
Checkpoint
./scripts/dev.shstarts backend and frontend together, and Ctrl+C stops both- A sample uploaded in the browser shows classification, extraction, issues, and a GL suggestion
- You can trace one button click from component to
api.tsto the FastAPI route it calls