# ResearchX **TL;DR** — ResearchX turns one question into a cited research report: pick a method, approve the plan, and the engine visits real web sources, with every claim anchored to a quoted passage from a page it actually read. The engine milestone (v0.2.5) is shipped and owner-approved; hosting goes live next (v0.3, in progress). - **What** — FastAPI backend (planning, search, extraction, verification, export gates) plus a Next.js frontend; the full test suite runs with zero API keys. - **Why** — Readers can check every citation back to its source passage; runs are resumable from durable state. - **Action** — no action — FYI. Developers: see the Quickstart below. - **Details** — `frontend/README.md` (web app); `.ciagent/` (pipeline state and roadmap). A deep-research studio where you bring your own model, storage, and memory; choose a research methodology before a single query fires; and every claim in the final report is anchored to a visited, verified, quotable source. **Status: MVP shipped (v0.2.5, owner-approved 2026-09-27)** — the full research engine (FastAPI + asyncio state machine + adapter layer + attack-proven trust loop) behind the v0.1 frontend. Live mode covers the core research loop (G-002/G-009): the engine runs locally per the quickstart; library/settings/suggestions/refine stay mock-backed until v0.3+. ## Quickstart ```bash # frontend (prototype, mock-backed — works today) cd frontend corepack pnpm install corepack pnpm dev # http://localhost:3000 # backend (MVP — uv manages Python 3.12 and the venv) cd backend uv sync # installs deps + editable researchx package uv run uvicorn researchx.api.app:app --reload --port 8000 uv run pytest # full suite, zero API keys ``` Three clicks from cold start to initiated research. ## Monorepo command map | Layer | Install | Dev | Gates | |---|---|---|---| | backend (uv, Python 3.12) | `uv sync` | `uv run uvicorn researchx.api.app:app --reload --port 8000` | `uv run ruff check src tests` · `uv run mypy src` · `uv run pytest` | | frontend (pnpm 12.6) | `corepack pnpm install` | `corepack pnpm dev` | `corepack pnpm typecheck` · `corepack pnpm lint` · `corepack pnpm test` · `corepack pnpm build` | ## Engine architecture (v0.2 P2) The research engine is a hand-rolled asyncio state machine: ``` PLANNING → AWAITING_APPROVAL → SEARCHING → EXTRACTING → REFLECTING ─┐ (halt until ↑ depth loop (dials.depth) │ approved) └────────────────────────────┘ → SYNTHESIZING → VERIFYING → DONE (+ CANCELLED / FAILED terminals) ``` - Durable state: `runs//state.json` rewritten atomically after every transition; resume re-enters at the recorded cursor with the persisted plan - Event bus: typed events (`run_started`, `query_fired`, `source_visited`, `passage_extracted`, `state_changed`, `run_done`, `run_error`) — trace per run - Visited-only constraint: the writer's context is the evidence store and nothing else - Trust loop: SSRF-guarded liveness (live/stale/dead), quote anchoring (normalize + fuzzy ≥ 0.85, weak matches flagged), citation ledger JSON, exportable verification checklist, single-source vs corroborated confidence labels Run artifacts per run: `state.json`, `report.md`, `ledger.json`, `checklist.md`. Try it on fakes (no keys): `uv run python -m researchx.engine.demo` ## Adversarial guarantees (v0.2 P4) Every trust-loop claim is attacked by the adversarial suite (`backend/tests/test_adversarial.py`) — these are tests that try to fool the verifier, and their passing proves the defenses: | Attack | Defense (proven) | |---|---| | Writer hallucinates a citation to a passage that was never stored | Unknown passage IDs count as **unanchored claims** in the ledger; the export gate fails the report | | Fabricated quote (matches nothing) | Anchoring verdict: no match, flagged — never silently accepted | | Near-miss quote (0.80–0.85 similarity) | Flagged `weak_match` in the ledger — surfaced, not accepted | | Liveness probe redirected to a private/metadata IP | Per-hop redirect re-guarding classifies it **dead** (SSRF refusal) | | Redirect chain ending at a gone page (410) | Classified **dead** | | Oversized page (5MB) response | 2MB read cap — classifies without unbounded memory | | Pause during the depth loop | Durable state persists; resume completes the run over the HTTP API | | Cancel mid-loop | Terminal CANCELLED state persisted, never swallowed | REQ-23 (≥99% citation liveness) is a per-run **export gate** enforced by the verification checklist, plus an opt-in keyed live tier (`cd backend && uv run pytest -m live`) — the zero-key CI suite never asserts it vacuously. The E2E happy path (≤3 clicks: ask, choose, approve) is validated against the real FastAPI + engine stack, defaults only — not the mock (`frontend/src/lib/api/e2e-real-stack.test.ts`). ## Adapter configuration (v0.2) All keys resolve from env files only (`.env`, `.env.secrets`, `.env.*` — never the shell environment, never git): | Variable | Scope | Used by | |---|---|---| | `OLLAMA_API_KEY` | model (cloud) | Ollama cloud endpoint `https://ollama.com/v1`; absent/keyless falls back to `http://localhost:11434/v1` automatically | | `OLLAMA_BASE_URL` | model | Overrides the default cloud base URL | | `TAVILY_API_KEY` | search | Tavily default search; absent falls back to the keyless DuckDuckGo adapter (degraded mode — surfaced in the run trace) | The test suite needs **no** keys: fake model/search adapters (REQ-31) drive every engine test. ## Live mode (v0.2 P3) The frontend runs mock-backed by default (works standalone, zero keys). To run the core research loop against the real backend: ```bash # terminal 1 — backend (fake adapters by default; real Ollama/Tavily adapters # are used by the engine in v0.2.5+ wiring — the served API currently ships # fake-adapter runs for demos and tests; see Adapter configuration below) cd backend && uv run uvicorn researchx.api.app:app --reload --port 8000 # terminal 2 — frontend in live mode cd frontend && NEXT_PUBLIC_RESEARCHX_API_MODE=live corepack pnpm dev ``` Live mode covers the core loop (Ask → Choose → approve plan → live trace → report + ledger); the library, settings persistence, suggestions, refine, and graph surfaces stay mock-backed until v0.3. The API surface: `POST /api/runs`, `GET /api/methodologies`, run lifecycle (`approve/pause/cancel/resume`), `GET /api/runs/{id}/events` (SSE), `GET /api/config`, report + ledger artifacts. ## What's in the prototype | Screen | What it does | |---|---| | `/ask` | One question box + one optional context box — plus two opt-in helpers below and beside them: open-question suggestions (follow-ups from your past runs, one click fills the box) and the Refine question dialogue (a few clarifying answers → a composed, sharper question). Neither ever auto-starts. | | `/choose` | All 8 locked methodology cards — mock ETA, cost estimate, why-recommended, and the 12 methodology dials behind each card's Advanced drawer. | | `/run/[id]` | Plan & Run: approve the research plan (sub-questions, source universe, outline), watch the live trace stream, pause/resume or cancel. | | `/run/[id]/report` | Reading Room: the report with page-anchored citations — hover or focus any `[n]` marker for the exact quoted passage — plus the citation ledger and Markdown/JSON exports. | | `/library` | Stored reports filed into auto-categorized folders (sortable, links into the Reading Room), the Connections panel (shared sources ⇄, related topics →), plus v2-preview project graph and task inbox. | | `/settings` | API-key status (masked, never raw), the reports storage directory, methodology defaults with the resolved 12 dials, appearance, and about/version. | The persistent shell (every screen): sidebar nav + adapter connection-status dots, top bar with the ⌘K navigation palette (screens + stored reports) and the avatar menu (local profile, Settings, About). All data flows through a typed `ApiClient` interface backed by `MockApiClient` — the same components will talk to the FastAPI backend via the OpenAPI-generated client in the MVP milestone, with zero rewrites. ## Scripts Run inside `frontend/`: | Command | Purpose | |---|---| | `pnpm dev` | Dev server on :3000 | | `pnpm build` | Production build | | `pnpm lint` | ESLint (flat config) | | `pnpm typecheck` | `tsc --noEmit` (strict) | | `pnpm test` | Vitest + Testing Library (jsdom) | ## Repository layout ``` frontend/ Next.js 15 App Router, TypeScript strict, Tailwind v4, Vitest .ciagent/ Pipeline state (roadmap, requirements, plans, checkpoints) ``` Milestone v0.2 (MVP: FastAPI backend, research engine, adapter layer, trust loop) begins only after this prototype is approved.