Datalumina

Getting started

Architecture and tooling

The monorepo, the locked stack, the two system paths, and how to work with coding agents on this project.

Before producing any code, you need to know how the services fit together, which technologies are locked in and why, and how the repo is set up so coding agents can work in it productively. All of it lives in two files you will keep coming back to, AGENTS.md at the repo root and docs/architecture.md.

The outcome of this stage is orientation. When it is done you can explain the two paths through the system, name every piece of the stack and its job, and know the working rhythm Dave uses with AI throughout the build.

daveebbelaar/document-copilot0:18:00

One repo, agent instructions at every level

Document Copilot is a monorepo. Frontend and backend live in a single GitHub repository.

document-copilot/
├── AGENTS.md           # source of truth for any coding agent
├── README.md
├── data/               # local corpus + download script (payloads gitignored)
├── docs/               # specs, briefs, design notes
├── backend/            # FastAPI service (see backend/AGENTS.md)
└── frontend/           # React SPA (see frontend/AGENTS.md)

The reason is coding agents. When both services live in one repo, the agent can reason over the entire context. It sees the API contract from both sides, and a change to a backend endpoint and the frontend call that uses it can happen in one session.

There are three AGENTS.md files. One sits at the root with universal rules, and backend/ and frontend/ each have their own with stack-specific rules. These get injected into the agent's context automatically. Cursor picks up AGENTS.md out of the box; if you follow along with Claude Code, rename each file to CLAUDE.md in the same locations.

The root file opens with the point of the whole exercise:

This file is the source of truth for any coding agent (Claude Code,
Cursor, Codex, etc.) working in this repo. Read it before touching code.

Dave notes in the video that these instruction files encode about three years of decisions from real client projects. Having them in place before the first prompt is a large part of why the AI output in this build stays consistent.

The locked stack

AGENTS.md pins the stack and states, "Stack is locked unless explicitly changed. Don't propose alternatives without a stated reason." That single line stops the agent from swapping technologies mid-build.

PieceChoiceWhy
BackendPython + FastAPIThe API layer; Python is where the AI and retrieval ecosystem lives
FrontendVite + React SPA + TypeScriptA plain single-page app; no Next.js, SSR, or server components
DatabaseSupabase Postgres + pgvectorOne database for users, chats, documents, chunks, and vectors; no separate vector store
AuthSupabase AuthEmail login and session handling without building auth yourself
LLM + embeddingsOpenAIGeneration and embedding models behind one SDK
OrchestrationPydantic AITyped agent boundaries: explicit dependencies, typed outputs, bounded tools
HostingRailwayOne frontend service, one stateless backend service, minimal ops

Supabase pulls double duty, and that is deliberate. It is a real Postgres database (so SQLAlchemy, Alembic, and full-text search all work normally) and it handles the entire sign-in flow. Railway is chosen over AWS or Azure because it keeps deployment simple enough for a tutorial while still producing a real production app; for client projects Dave's team uses the major cloud providers, but that depth would overload this build.

Two paths through the system

docs/architecture.md opens with a service-level diagram, and the main thing to see is that there are two separate paths:

  1. The live chat path. Analyst opens the browser app (served by Railway), signs in through Supabase Auth, and sends a chat request with their JWT to the FastAPI backend. The backend verifies the user, retrieves passages from Postgres, generates a grounded answer with OpenAI, streams it back with citations, and persists the conversation.
  2. The ingestion path. A local, offline pipeline takes the downloaded SEC filings, parses and chunks them, creates embeddings with OpenAI, and stores documents and chunks in Supabase. There is no online upload or syncing mechanism. The corpus is prepared once, locally, and the live app only reads it.

Keeping these separate keeps the backend stateless and the deployment simple. Everything durable lives in Supabase, so Railway services can restart freely. Dave reasons through the diagram from the user story ("an analyst opens the browser") and recommends you do the same whenever you read an architecture diagram.

The boundaries are strict. The browser stays thin. It renders chat state and holds the user's session, and it never sees the service role key or calls OpenAI directly. The backend is authoritative. Retrieval, grounding, citation checks, and database writes all happen in FastAPI.

Dependency guardrails

The root AGENTS.md sets a hard default, "write it yourself. Reach for a library only when the alternative would be non-trivial, error-prone, or reinvention of a standard." Every dependency costs you something in bundle size, supply-chain risk, and future upgrade work.

The project goes one step further and enforces this at the package-manager level, a direct response to the recent npm supply chain attacks. The frontend sets this in frontend/.npmrc:

# Block packages younger than 7 days (10080 minutes). Defends against
# the typosquat / compromised-release window where a newly published
# version of a popular package turns out to be malicious or yanked.
minimum-release-age=10080

And the backend mirrors it in backend/pyproject.toml, alongside exact version pins:

[tool.uv]
add-bounds = "exact"
exclude-newer = "7 days"

Together these mean no package published in the last seven days can enter the project, and every Python dependency is pinned to an exact version. A freshly compromised release of a popular package simply cannot be installed during its most dangerous window.

These guardrails are pre-configured

Both files ship in the starter repo on main. Do not remove them when dependency installs feel slow or a brand-new package version is unavailable. That friction is the feature.

How to work with AI on this project

Dave is explicit about the working style for the whole build, and it is the opposite of firing off one huge plan and letting an agent run for two hours:

  • Small, incremental, bite-size prompts. When you deeply understand the problem, short focused prompts are usually faster than mega-plans, and you stay in control of the code.
  • Plan before building. The next stage starts by generating docs/todos.md, a phased checklist, before any application code exists.
  • Intermediate review points. In Dave's words, no matter which skills, prompts, or models you use, "if you don't have intermediate review points and decide what good looks like, it's going to get messy." Read the diff after every step.
  • The architecture doc is the map. docs/architecture.md is referenced again and again throughout the build, both by you and in prompts to the agent, so decisions stay anchored to the design instead of drifting.

The AGENTS.md files do the standing-instruction work (stack, dependency policy, config rules, code style), which frees your prompts to be short and task-specific.

Checkpoint

Before moving on, make sure you can answer:

  • Why is this a monorepo, and where do the three AGENTS.md files live (and what do you rename them to for Claude Code)?
  • What are the two paths through the system, and why is ingestion a local offline pipeline?
  • Which two files enforce the 7-day dependency age rule, and what attack does it defend against?
  • What does the backend own that the browser must never do?

Next: Setup

Create the Supabase project, configure environment files, download the SEC corpus, and generate the build checklist.

On this page