# Phase 5 — Mode B console (the AI overlay) **Goal:** a prompt box that streams a short, cited answer and can take over the stage panel. The AI is a second driver for the same panel, not a second site. **Depends on:** Phase 3 (live URL), Phase 4 (corpus). --- ## Work ### 5.1 Query path ``` question → rate limit (per IP) → CLASSIFY fast tier intent · injection score · off-topic · PII ├─ refuse → fixed message, zero model spend └─ allow → ROUTE fast tier which area(s) to search → RETRIEVE Qdrant (payload-filtered) + SQLite FTS5 → reciprocal rank fusion → cross-encoder rerank top-20 → top-5 → COMPOSE mid tier answer ONLY from context, inline citations → VERIFY citation presence + claim supported by context → STREAM SSE ``` Four nodes. If the graph needs more than four to work, stop and report — complexity growth here is the failure mode this project exists to avoid. ### 5.2 Transport - `GET /api/ask` as Server-Sent Events. - Three event types: `token`, `meta` (route, timings, tokens, cost), `done`. - **Never buffer SSE.** Verify with `flush_interval -1` on the proxy if streams stall. No gzip on the API path. ### 5.3 The console (the only client-side code in the project) One hand-written TypeScript island, ES modules, no framework, no bundler. - `xterm.js` console showing the pipeline live: route, retrieval count, timings, tokens, cost. - Prompt box with submit and stop. - Stage panel driven by the `token` stream — the driver built in Phase 2 §2.4. - Panel clears on new response and on navigation. - Footer strip: model · latency · tokens · cost. ### 5.4 Citations - Every factual claim carries a citation to a `content/` page or artefact. - Citation URLs validated against the known-artefact allow-list; unknown URLs dropped, not rendered. - Answer shape: direct answer → specifics → evidence pointer → optional single suggestion (per `agents/persona.md`). ### 5.5 Persona - Load `agents/persona.md` at runtime. It is a file, versioned, not a string in code. - Test the tone: a short answer, an honest "not in the corpus" answer, and a "here's the better example for your role" answer. --- ## Definition of Done - [ ] First token < 4 s; complete answer < 15 s at p90, measured and recorded. - [ ] Every claim in ten sample answers resolves to a real page or artefact. No dangling citations. - [ ] The panel is driven by the SSE stream without changes to layout code (proves Phase 2 §2.4 held). - [ ] The island is the **only** client-side application code; no framework, no bundler, no `node_modules`. - [ ] A and B do not drift: for any question, the answer's claims exist on the site. - [ ] SSE streams untruncated at p90 with no cancellation leak when the client disconnects. - [ ] All model calls go through OmniRoute; grep proves no direct provider call exists. - [ ] Asking an off-topic question produces a short refusal and costs nothing. --- ## Out of scope - Guardrail hardening, cost ceilings and the kill switch — **Phase 6**. Phase 5 runs with a permissive guard and no public traffic. - Voice input. - Hub `/ask` registration (a follow-on task, not this phase). --- ## Risks | Risk | Mitigation | |---|---| | Graph grows past four nodes | Hard review gate: report and justify before adding a fifth | | Panel refactor needed | Phase 2 §2.4 contract; if it fails, fix the panel contract, do not fork the UI | | Streaming stalls behind Caddy | Explicit flush settings; p90 measurement, not a spot check | | Answers drift from the site | Citation verification is a node, not a hope | --- ## 5.6 Citations point at the original An answer cites where it came from, not a curated page standing in for it. | Source | Cited as | |---|---| | Gitea repo file | full URL to the file in Gitea | | Vault note | full URL to the note in `sam/obsidian-vault` | | Graph answer | the note title the node came from, plus its URL | | Curated claim | the site page | A citation whose URL cannot be opened is dropped, not rendered. A claim with no traceable origin is not returned at all. ## 5.7 Curated outranks harvested `content/` is tier 1; everything harvested is tier 2. When the two disagree, the tier 1 claim wins and the answer says so plainly: the curated description is current; the older text is background. This is the rule that stops a stale README from quietly overwriting Sam's own description of his work. ## 5.8 The live source readout While the answer streams, a panel shows what is being read, named as the visitor would recognise it: ``` reading · gitea/photo-pipeline/README.md · obsidian/210 AI Resume/Graphify.md · graph · community 42 · vault infrastructure ``` This is a feature, not a debug view. It answers the question every recruiter has and does not ask: is this making things up? Show the working — it is also the most convincing demonstration of the system on the page.