Skip to content

RFC-011 — Assistant conversation surface

RFC-011 — Assistant conversation surface (v0.7)

Section titled “RFC-011 — Assistant conversation surface (v0.7)”

Before v0.7, /assistant shipped as a single-turn “run” surface (seed-2df4). The IA conflated runs with conversations: the left rail listed the eight most-recent answer_runs; clicking one replaced the in-memory message list with that single turn alone. With migration 038_assistant_conversations already on main and the SSE handler at cmd/atlas-gateway/assistant_stream.go auto-creating a conversation if conversationId is absent, the backend modelled real multi-turn threads — but the UI never surfaced them.

A senior visual + interaction audit on top of that surface identified 25+ issues spanning a P0 token bug (text-loss referenced an undefined CSS variable so error banners rendered invisible), a generic empty state that hid the seven composer modes, a debug-style right rail, citation list with no anchor linkage, missing typing/streaming feedback, load-bearing refs duplicating state the reducer should have owned, no mobile layout, no error recovery, no tests.

The redesign closes those gaps in nine landing phases (seed-c7a1 through seed-c7a6), now all live.

  1. Conversations are the unit of history. The left rail lists assistant_conversations ordered by updated_at; clicking a thread sets ?c=<conversationId> and primes the composer to append a new turn to that thread. The SSE stream auto-creates a conversation on first submit when ?c= is absent; run_created events promote the new id into the URL so the thread is shareable + resumable cross-device. Backed by the new ?conversationId=X filter on GET /api/atlas/runs (Phase 5a, ordered by turn_index ASC) and the CRUD endpoints that landed in seed-2df4. No feature flag — the new IA is the canonical layout; the legacy run-rail layout was deleted.
  2. Inline citations + EvidenceInspector. The right rail’s flat numbered citation list is replaced with djed-evidence-ui’s EvidenceInspector rendered from a Claim aggregated by answerClaimFromRun. Source tier + freshness chips, lineage tab, and per-claim provenance all surface; clicking a citation fires assistant-citation-clicked telemetry. Inline [¹] superscript anchors inside the answer prose are queued for the djed-ai-copilot 1.1.0 publish (it needs a renderAssistantContent slot); the rail-side inspector ships in Phase 4.
  3. Plain-language live status. The raw djed AssistantToolTrace (rendered “Backend: X · Yms” stage names) is replaced by AssistantLiveStatus — a vertical timeline of narrative steps (“Reading your question” → “Searching SAP knowledge (3 sources)” → “Drafting cited answer (claude-opus-4-7)”) emitted by the pure assistantStepCopy.ts builders. Motion-aware (framer-motion with prefers-reduced-motion honored), uses Atlas tokens only.
  4. Empty state teaches the surface. AssistantHero renders all seven ATLAS_COMPOSER_MODES as cards with two example prompts each. Clicking an example pre-fills the composer + selects the mode + fires assistant-mode-changed telemetry with source: 'empty_state'. The previous boilerplate (“Atlas workbench / Ask a source-grounded SAP migration question”) is gone.
  5. Reducer owns ephemerality. pendingAssistantId + turnFinalized moved from load-bearing useRefs into AssistantStreamState. New idempotent actions: submit_turn(pendingAssistantId), finalize_turn (no-op on re-dispatch), discard_pending. transport_error also clears the pending id so onError paths drop the orphan bubble cleanly. AssistantView.tsx has zero useRef declarations for streaming state.
  6. Mobile parity via WorkbenchTriPane. The tri-pane primitive collapses to a single-column on phone; leftDrawer + rightDrawer slots mount ResponsiveSheet-wrapped Conversations and Run-details buttons so both rails are one tap away. Selecting a thread from the phone sheet auto-closes the drawer.
  7. Keyboard + error recovery. Surface-scoped shortcuts: Esc aborts the in-flight stream; / focuses the composer (skipped when an editable element already has focus). On any non-done terminal phase, AssistantErrorRecovery mounts between the message list and the composer with phase-specific actions: Retry (re-runs the last query in its prior mode via lastSubmittedRef), Try different mode (dropdown of the six non-current modes), Start new conversation (clears state + unsets ?c=).
  8. Externalized copy. Every user-facing English string lives in assistantStrings.ts (groups: surface, rail, composer, badge, error, history). The future i18n channel swaps the export without touching component code.
  9. Eslint guard rails. LEGACY_TOKEN_PATTERNS extended to forbid text-/bg-/border-(loss|profit|info|caution) phantom semantic tokens — these have no --color-* mapping in atlas-design/tokens.css so they silently render unstyled. A second guard forbids inline h-[calc(100vh-Nrem)] height arithmetic in surface files.
  • Legacy URL contract preserved. ?runId=X deep links continue to resolve: on mount, AssistantView fetches the run, reads its conversationId, and redirects to ?c=<convId>&runId=X with a two-week console.warn deprecation. The legacy-redirect telemetry event lets us measure the migration tail. After the deprecation window, the redirect goes silent.
  • Telemetry stream gains assistant-* events. assistant-turn-submitted, -turn-completed, -turn-aborted, -feedback-submitted, -citation-clicked, -mode-changed, -history-opened, -conversation-started, -conversation-selected, -conversation-archived, -conversation-renamed. All flow through reportAtlasEventatlas-ui-gateway → existing analytics pipeline. No new dashboard config is required to ship — consumers query the raw event stream; a dedicated funnel dashboard can be added in a follow-up if usage signals demand it.
  • Test coverage. assistantStream.test.ts covers nine reducer transitions including the idempotence guarantees; assistantWorkbench.test.ts and assistantApi.test.ts cover the cache-state helpers + composer body. AssistantView end-to-end tests and Playwright visual baselines for the /assistant chrome are queued in Phase 7 (this RFC’s companion); the build-surface playwright pattern (real-backend, no mocks, realBrowserProofEnabled() gate, clipped to stable region) carries over directly.
  • Upstream package extensions for inline-[¹] + multiline composer. djed-ai-copilot 1.1.0 will add renderAssistantContent?: (msg) => ReactNode to AssistantMessageListProps; djed-evidence-ui next-minor adds multiline?: boolean to QueryComposerProps (autosize <textarea> with Shift+Enter newline + Cmd/Ctrl+Enter submit). Atlas wires both via the existing file: link during dev and adopts after publish.

Accepted — implemented across seed-c7a1 → seed-c7a6 and deployed live on 2026-05-21.

PhaseSeedSubject
0seed-c7a1P0 bug + telemetry shim + eslint guard
1seed-c7a1AssistantHero empty-state capability surface
2seed-c7a1Streaming feedback grammar (typing dot, live status, model badge)
3seed-c7a1Reducer consolidation + externalized copy
4seed-c7a1EvidenceInspector adoption
Aseed-c7a2Post-deploy polish — caution token + banned-terms allowlist
Bseed-c7a3Build-stack landing (evidence hydration, artifact polish, stage inspector)
Dseed-c7a4Wire-a backend reconciliation
Eseed-c7a5Visual baselines rewrite (real backend, no mocks)
5seed-c7a6Conversation-first IA
6aseed-c7a6Mobile drawers + keyboard shortcuts + error recovery

Phase 6b (multiline composer, requires djed-evidence-ui publish) and Phase 7 (full test scaffold including Playwright assistant baselines) are queued as follow-ups; both are additive and do not block the IA shift.