Imported from saayush615/DevDocs (
AGENTS.md). Install upstream withnpx skills add saayush615/DevDocs. Copyright stays with the author.
AGENTS.md
docs/PRD.md is the source of truth for architecture, scope, and phase boundaries — read it fully before implementing anything.
Three separate packages, not a pnpm workspace: frontend/ (Next.js), backend/ (Express 5), ai-service/ (FastAPI). Each has its own package.json + pnpm-lock.yaml. Install and run per package with pnpm.
Never read or expose .env files
- Never read, print, or commit any
.envfile (e.g.backend/.env,frontend/.env,ai-service/app/.env) — they contain real secrets (DB credentials, better-auth secret,GOOGLE_API_KEY,SERVICE_TOKEN). - To see what env variables a package needs, read only the
.env.exampletemplate:backend/.env.example,frontend/.env.example,ai-service/app/.env.example(all placeholder/empty values). - Never log, echo, or paste values that look like secrets, and never add a real
.envto a commit.
Current state (verified — MVP delivered, per PRD §8)
- Auth: better-auth mounted at
/api/auth/*(backend/src/index.ts); frontend uses the better-auth React client (frontend/lib/auth-client.ts) hitting backend directly. - Upload:
.md/.txtvia multer (2 MB cap) →services/upload.service.ts→ FastAPI/ingest; status runspending → chunking → embedded | failed. - Chat:
controllers/chat.controller.tspersists the user message, proxies the FastAPI SSE stream, persists the assistant message with citations +routeTaken. Auth'd routes are/api/document,/api/conversation,/api/chat(all guarded bymiddleware/requireAuth.ts→req.userId). - AI service: real LangGraph implementation, not a stub. Linear graph
classify_q → simple_rag → grounding_check → final_answerinai-service/app/graph/(graph.py,node.py,state.py).app/routers/ingest.py+query.py(JSON +/query/streamSSE). Qdrant needs to be running —docker-compose.ymlnow runs both Postgres 17 (port 5432) and Qdrant (port 6333). - Schema:
backend/prisma/schema.prismaalready has the domain tables —Document(withDocumentStatusenum),Conversation,Message(citations Json?,routeTaken) — alongside the better-auth tables. There is noUsageDailytable or quota code yet. - No test setup or CI anywhere. Definition of done =
lint+format+ typecheck (pnpm buildfor backend) per package.
Commands
Backend (backend/):
pnpm dev—tsx watch src/index.ts, port 3001pnpm build—tsc(typecheck + emit todist/); there is no separate typecheck scriptpnpm lint/pnpm format/pnpm format:checkpnpm prisma ...— Prisma 7 CLI (generate,migrate dev, etc.)
Frontend (frontend/):
pnpm dev—next dev, port 3000pnpm lint/pnpm format/pnpm build(build is the typecheck)
AI service (ai-service/): Python 3.14 venv at ai-service/.venv; run uvicorn app.main:app (default port 8000) from ai-service/. requirements.txt is a pip-freeze-style lock, kept current via uv.
Prisma 7 quirks
- Generator
prisma-clientoutputs tobackend/src/generated/prisma, which is gitignored — runpnpm prisma generateafter cloning beforetsc/dev works. - Backend imports the client from
../generated/prisma/client.jsand uses the@prisma/adapter-pgdriver adapter (backend/src/lib/prisma.ts). prisma.config.tsloads dotenv and readsDATABASE_URL; migrations live inprisma/migrations/. New domain-schema changes needpnpm prisma migrate dev(no migration exists for the current schema — verify withgit statusbefore assuming).
Backend gotchas
- ESM (
module: nodenext+verbatimModuleSyntax+exactOptionalPropertyTypes): relative imports must use explicit.jsextensions (import { auth } from './lib/auth.js'). package.jsonhas no plaintypescriptdep — it uses the aliasednpm:@typescript/typescript6package. Don't "fix" this by adding vanilla typescript or bumping the version.- Required env (
backend/.env, template in.env.example):DATABASE_URL,BETTER_AUTH_SECRET,BETTER_AUTH_URL,FRONTEND_URL,AI_SERVICE_URL,AI_SERVICE_TOKEN(must match ai-serviceSERVICE_TOKEN). - better-auth cookie prefix is
devdocs, secure cookies off (local dev); CORS allowshttp://localhost:3000with credentials. - Frontend has no Next proxy/rewrite — the auth client hits
http://localhost:3001directly viaNEXT_PUBLIC_BACKEND_URL. - Upload: AI
/ingestreturns HTTP 200 even on failure —upload.service.tsmust checkstatus !== 'embedded'; any exception flips the doc tofailed(never leave it stuck atchunking). - Known latent bug:
services/aiClient.service.tssendstok_k: 5(misspelled) instead oftop_k— FastAPI silently uses its default of 5.
AI-service gotchas
- Required env (
ai-service/app/.env, template atapp/.env.example— note it is insideapp/, not the package root):GOOGLE_API_KEY,SERVICE_TOKEN. Defaults inconfig.py:EMBEDDING_MODEL=gemini-embedding-001,EMBEDDING_DIM=768,LLM_MODEL=gemini-3.6-flash. Qdrant collectiondoc_chunks(created at startup with auser_idkeyword payload index). /query/streamis not true token streaming: it runs the graph once viaainvoke(), then re-emits the final answer word-by-word.graph.astream()per-token streaming is a known stretch goal (see comment inrouters/query.py).- The graph is compiled once at import (
_graph) inquery.py, not per request. multi_hopis only a soft fallback today:simple_ragjust doublestop_k; the real multi-hop branch is V1 (PRD §4/§9).- Retrieval isolation lives in
services/vector_store.pysearch_chunks()via a harduser_idfilter — keep the filter mandatory, never weaken it.
Prettier differs per package
- Backend: single quotes. Frontend: double quotes +
prettier-plugin-tailwindcss. Runformatinside each package; do not apply one style repo-wide.
Architecture (non-negotiable, PRD §3)
- The frontend never calls the AI service directly. Next.js → Node (auth + policy + system of record) → FastAPI with a service-to-service bearer token; FastAPI trusts only requests carrying it (
app/auth.py). - Retrieval isolation: every Qdrant query is filtered by
user_id— no cross-user data leakage, ever. (V1 addsorganization_idto the filter.) - Chat streams via SSE FastAPI → Node → client — not buffered.
- Every answer includes text, citations (
document_id,chunk_index, snippet), androute_taken(simple|multi_hop). The mandatory grounding check fails closed: unsupported claims → "I don't have enough information in the available documents." - Forms on the frontend use react-hook-form + zod (
@/*path alias maps to the frontend root, notsrc/).
Scope discipline (PRD §9)
- Build V0 next (Guardrails): per-user daily quota + burst rate limiting (chat/upload/auth),
UsageDailymetering (FastAPI reports usage, Node enforces),429surfaced in the UI. Acceptance (PRD §9): 31st query of a day rejected before any token is spent. - Do not build yet: PDF/GitHub/URL ingestion, multi-hop branch, org tenancy (
organization_id), eval harness (V1); Slack/Discord (V2); RBAC/departments/admin UI (V3); MCP server (V4); memory layer / email verification / WhatsApp (deferred, PRD §10).