Squash-merge of phase/08-final-review-approval-ship into milestone/v0.1-prototype. Review PASS-WITH-FIXES (double-compose + drawer-keyboard P1s, 90/90). Audit PASS-WITH-NOTES (all applied). Tag v0.1.8 = extended-prototype release candidate. Milestone merge to main HALTS at the REQ-62 owner approval gate.
7.9 KiB
Architecture
Overview
Two-milestone build (owner-directed 2026-09-27). Milestone v0.1 builds the UI/UX prototype as the production Next.js frontend with all data access behind a typed ApiClient interface backed by MockApiClient — no backend, zero throwaway work. Milestone v0.2 (gated on prototype approval) adds the FastAPI backend, the custom async research engine, and the adapter layer, then swaps MockApiClient for the OpenAPI-generated client. The engine is a hand-rolled asyncio state machine; every run persists its resolved 12-dial configuration and full event trace for reproducibility and auditability.
Components
1. Methodology Framework (src/researchx/config/, src/researchx/methodologies/)
Typed pydantic schema for the 12 dials (source universe, search routing, query strategy, breadth, depth, quality gates, citation rigor, verification passes, synthesis style, output structure, memory scope, checkpoints) with safe defaults. A methodology is a named dial bundle + planner/writer prompt templates + output shape. Registry resolves methodologies by slug; every run snapshots resolved dial values.
- Boundaries: pure data + prompts, no I/O. Depends on nothing internal.
- DependsOn: none
2. Adapter Layer (src/researchx/adapters/)
Five pluggable slots with versioned contracts:
-
Model: any OpenAI-compatible endpoint (covers Ollama cloud/local and vLLM); separate planner/writer roles; capability probe on connect (context window estimate, conservative request cap = min(model claim, 8192), embedding batch 8) surfaced as UI overrides; structured-output capability warning. Default: ollama.com/v1 cloud-first with localhost:11434/v1 fallback.
-
Search: Tavily default; DuckDuckGo keyless fallback; interface ready for Exa/Perplexity later.
-
Storage: local filesystem adapter; reports as plain Markdown/JSON under user-chosen directory; run state as JSON after each transition.
-
Memory: off in v1.0 — interface stubbed for semantic caching.
-
Secrets: env-file provider in v1.0 — interface stubbed for OpenBao.
-
Boundaries: all external I/O lives here; engine never imports vendor SDKs directly.
-
DependsOn: config (dial schemas only)
3. Research Engine (src/researchx/engine/)
Custom asyncio state machine: PLANNING → AWAITING_APPROVAL → SEARCHING → EXTRACTING → REFLECTING → (depth loop) → SYNTHESIZING → VERIFYING → DONE. One RunActor per run; durable JSON state persisted to storage after each transition (pause/cancel/resume); typed event bus feeding SSE live trace; plan (sub-questions + source universe + outline) halts at AWAITING_APPROVAL until approved/edited.
- Boundaries: orchestrates; contains no HTTP handling and no vendor I/O.
- DependsOn: adapters, methodologies, trust, evidence
4. Evidence Store (src/researchx/evidence/)
Visited-page store with stable passage IDs; passage extraction from fetched pages. The only source of writer context — the visited-only citation constraint is enforced by construction: the writer sees nothing else.
- Boundaries: owns evidence data model.
- DependsOn: adapters (search/storage only)
5. Trust Loop (src/researchx/trust/)
Runs inside every methodology: generation-time visited-only constraint [E1]; post-run URL liveness pass with live/stale/dead classification [E1]; claim-to-passage quote anchoring (fuzzy match against stored passages) [E1b]; citation ledger JSON per run (URL, visited_at, passages, claims supported, health, authority tier); verification checklist export.
- Boundaries: read side of evidence store; verification reports feed the ledger.
- DependsOn: evidence, adapters (model for verification passes)
6. API Layer (src/researchx/api/)
FastAPI routes: POST /api/runs, GET /api/methodologies, GET /api/runs/{id}, approve/pause/cancel/resume, GET /api/runs/{id}/events (SSE), GET /api/config. OpenAPI schema generates the typed frontend client.
- Boundaries: transport only — no business logic.
- DependsOn: engine, methodologies, config
7. Frontend (Next.js App Router, TypeScript strict)
App shell (root layout, P5): persistent sidebar (primary nav — New research / Library / Settings; adapter connection-status dots as a v2 preview) + top bar (⌘K navigation palette — screens + stored reports; avatar button → user menu with the local single-user profile). Screens: Ask (question + optional context; open-question suggestions below the form — follow-ups from unanswered questions of past runs, click-fills-the-box, REQ-67; opt-in Refine dialogue — inline drawer, scripted clarifying exchange, composed refined question + suggested context, REQ-68), Choose (methodology cards with ETA/cost/why-recommended; dials behind "advanced"), Plan & Run (approve/edit plan, live SSE trace, pause/cancel), Reading Room v0 (rendered report, hover-a-citation → exact quoted passage, ledger panel, Markdown/JSON export), Library (P5: folder-structure report storage — auto-categorized folders, recency/title sorting, report cards into the Reading Room; cross-report Connections panel — shared-sources ⇄ / related-topic → edges; project graph + task inbox as v2-preview panels), Settings (P5: masked API-key status, storage directory, default methodology + resolved 12-dial display, appearance preview, about/version).
- Boundaries: consumes the typed
ApiClientinterface only — components never import the mock client directly. In v0.1 the interface is bound toMockApiClient(fixture data + scripted event replay); in v0.2 phase 3 the binding swaps to the OpenAPI-generated client with no component changes. - DependsOn: ApiClient contract (v0.1: mock; v0.2: API layer)
Data Flow
v0.1 (prototype): Ask (question + context) → Choose (methodology slug + dial overrides) → MockApiClient returns scripted plan → Plan & Run (scripted live trace replay, pause/cancel) → Reading Room (mock report with anchored citations, ledger, real file exports). All flows exercise the exact component paths the MVP will use.
v0.2 (MVP): Ask (question + context) → Choose (methodology slug + dial overrides) → POST /api/runs → engine PLANNING (planner model via model adapter, dial bundle resolved) → AWAITING_APPROVAL (plan persisted; SSE events; user approves/edits) → SEARCHING (search adapter, routing per dials) → EXTRACTING (fetch pages → evidence store, stable passage IDs) → REFLECTING (planner model; depth loop per dials) → SYNTHESIZING (writer model; context = evidence passages only, per-claim citation markers) → VERIFYING (liveness pass, quote anchoring, confidence labels) → DONE (report + ledger + checklist persisted to storage; Reading Room renders; export).
Build Order
Milestone v0.1 (prototype): 1. Frontend scaffold + ApiClient/MockApiClient boundary → 2. Ask + Choose → 3. Plan & Run + Reading Room v0 → 4. v2 mockups + polish + 3-click validation → 5. review + owner approval gate (owner returned gate feedback → extension cycle: 5. app shell + research library + settings + avatar → 6. final review + approval re-presentation (2nd owner gate feedback: 7. ask suggestions + refine dialogue → 8. final re-review + ship)).
Milestone v0.2 (MVP, gated on v0.1 approval):
- Methodology framework (dial schema + 2 seed methodologies + registry)
- Adapter layer (model/search/storage contracts + local implementations; fake adapters for tests)
- Evidence store (visited pages, passage extraction)
- Trust loop (liveness, anchoring, ledger)
- Engine (state machine, event bus, durable state, pause/resume)
- API layer (routes + SSE)
- Integration (swap MockApiClient → OpenAPI-generated client)
- Adversarial test suite (hallucinated-URL detection, quote-mismatch detection, pause/resume mid-depth-loop) + E2E happy path
Build order is dependency-driven: trust verification requires evidence; engine requires adapters + trust; API requires engine; integration requires API; frontend components carry over unchanged from v0.1.