# Architecture ## Flow ``` question ─▶ POST /api/research ─▶ createSession ─▶ runSession (in-process) │ ┌─────────────────────────────────────────┤ │ orchestrator loop (packages/agent) │ │ Claude decides: search / fetch / │ │ read_source / claims / evidence / │ │ contradictions / finish │ │ App enforces: budgets, schemas, SSRF │ └───────────────┬─────────────────────────┘ ▼ ResearchState (packages/research) every mutation = 1 DB write + 1 event ▼ PostgreSQL (packages/db) ▼ GET /api/research/:id/stream (SSE, Last-Event-ID replay) ▼ event-sourced UI (apps/web) ``` ## Key decisions - **No fixed pipeline.** The model chooses strategy via tool use; the app owns safety (budgets in `packages/shared/src/budgets.ts`, zod-validated tool inputs, SSRF guard). - **ResearchState is durable** and lives in PostgreSQL, outside the model's context window. The `ResearchState` service is the single mutation path: state write and event append always happen together, so the UI can never show fabricated progress. - **Event-sourced UI.** The client rebuilds its entire view by replaying the event stream from seq 0 — the same mechanism gives live streaming, reconnection (`Last-Event-ID`), and full session replay for debugging. - **Citations are mechanical.** At synthesis time, sources carrying evidence receive stable indices (`sources.assignCitationIndices`, ordered by first evidence use). The synthesis model may only use the `[n]` markers provided; the UI links them back to sources. - **In-process runner** (`apps/web/lib/runner.ts`): sessions run inside the Next.js server process for the MVP. A queue (e.g. Redis-backed) only gets added when scale demands it. ## Model roles Configured via env (`CLAUDE_ORCHESTRATOR_MODEL`, `CLAUDE_SYNTHESIS_MODEL`, …), all defaulting to `claude-opus-5`. The researcher/verifier roles are reserved for the post-MVP multi-agent phase (Explorer, Skeptic, Verifier, …) which only begins once the single-orchestrator engine is demonstrably reliable.