[feat-005] Multi-pass research orchestrator + UI integration

↗ view on GitHub · Nick Whitehouse · 2026-05-04 · a4adcbf3

Trigger: chat.ts auto-routes to the orchestrator when the user has any
research source selected (sources.legal non-empty OR sources.web=true).
Five-pass pipeline with budget enforcement (≤25 Olava calls, ≤45s wall),
designed fresh for Olava-001 (Qwen3.6 + LoRA) per the SLM-cost-advantage
strategy. work___'s services/{orchestrator,sub_agent,loop_controller}.py
served as architectural reference, not a port.

Backend (new)
- backend/src/lib/research/types.ts        - shared event/result types
- backend/src/lib/research/budget.ts       - call counter + wall-clock cap
- backend/src/lib/research/queryExpander.ts - pass 1: 1 Olava call → 3-6 specialised queries
- backend/src/lib/research/searchFanOut.ts - pass 2: parallel legal/web searches, dedupe by URL
- backend/src/lib/research/triage.ts        - pass 3: 1 Olava call → top-N most relevant
- backend/src/lib/research/extractor.ts     - pass 4: N parallel Olava calls → tailored extracts
- backend/src/lib/research/synthesizer.ts   - pass 5: streaming Olava call → markdown answer
- backend/src/lib/research/orchestrator.ts  - pipeline coordinator + SSE event emission

Backend (modified)
- routes/chat.ts: auto-detect research mode and route to runResearchOrchestrator
- lib/llm/olava.ts: forward delta.reasoning(_content) via onReasoningDelta
  so the UI shows a live "Thinking..." indicator during long Olava think-times
  (rather than dead air). Persisted as part of chat_messages - same scope as
  the response itself.

Frontend (new)
- src/components/chat/onit-status-icon.tsx - Drift Grid 3×3 ripple loader
  (ported from work___ UI System UPGRADE/loaders.jsx) cross-fades to the
  Onit O logo (#00112c) when streaming stops. Replaces MikeIcon in the
  assistant ResponseStatus.
- globals.css: gridPulse keyframe.

Frontend (modified)
- AssistantMessage.tsx
  - ResearchStepBlock: dot + label + meta detail (e.g. "top 5 selected"),
    rendered inline inside the existing PreResponseWrapper alongside other
    tool chatter. Wrapper auto-collapses to "Completed in N steps" once
    synthesis content arrives.
  - ReferenceBlock: numbered (1. 2. 3.) and indented under "Ranked results",
    with a continuous parent-x vertical line drawing through them so they
    visually nest under the parent step. No per-reference dots or in-between
    connectors. Click opens URL in new tab.
  - bug-003 obsoleted: reference_added events now flow through the wrapper
    again (single-pass mode no longer fires search tools - that's all
    research-mode now where synthesis is reliably non-empty).
  - Empty/whitespace-only content events no longer split wrappers (Olava
    sometimes emits "\n\n" between reasoning blocks; without this guard the
    UI breaks into two separate "Completed in N steps" cards).
- shared/types.ts: AssistantEvent extended with research_step + sources.web.
- hooks/useAssistantChat.ts: research_step SSE handler dedupes by key in
  the events array (so reload doesn't show running+done duplicates of the
  same step). Transient research.* events are kept out of persistence;
  research_step drives the UI.

Process
- backlog.md: full feat-005 design + feat-006 (citation reliability via
  add_citation tool - defer per testing notes 2026-05-04) appended.

Smoke-tested end-to-end against real Olava + Brave + CourtListener with
"What is the latest court case where a lawyer had misused AI in court?":
~30s wall, 16 events, 2KB synthesis with inline [Title](URL) citations to
the April 2026 Sullivan & Cromwell case (vs single-pass Olava emitting
just "\n\n" with the same prompt).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Repository nwhitehouse/mike
Author Nick Whitehouse <nick.whitehouse@mccarthyfinch.com>
Authored
Parents 3785d7f2
Stats 16 files changed , +1332 , -81
Part of Multi-pass research orchestrator (feat-005)

Capture this commit into my fork

Download a Markdown prompt that tells Claude how to port this exact commit into your working tree. Run it via claude -p < capture-commit-a4adcbf3.md from inside the repo you want the change in.

⬇ Download capture-commit-a4adcbf3.md