Skip to content

[Story]: Chat → Buddy migration — retire /chat, Buddy becomes the only conversation surface #319

Description

@daniilperkin

Chat → Buddy migration: one story, every piece of work below it. Sub-issues are listed in the order they should be done; each one's "blocked by" link shows what it waits on.

# Issue What Team (author)
1 sprintstart-ai#206 Filters, grep, streamed reasoning in the Buddy turn AI
1b sprintstart-ai#209 FAQ prompts accept Buddy questions, drop action/mentor requests AI
2 sprintstart-backend#214 Named conversations, drop uq_buddy_sessions_user, filters, reasoning events, FAQ question source Backend (LinseCed)
3 sprintstart-backend#259 Saved citations, message delete, isIncomplete, binning, conversations replace visits, chat history backfill Backend
4 sprintstart-frontend#266 Buddy gets chat's features: citations, Stop, queue, reasoning, searchable rail, re-point chat callers Frontend
5 sprintstart-frontend#206 One surface, capability switch, redirects, delete chat Frontend (LinseCed)
6 uni-cluster#10 Demo seed creates Buddy conversations instead of chats DevOps
7 sprintstart-backend#260 Remove the backend chat module Backend
8 sprintstart-ai#207 Remove /api/v1/chat, ChatAgent and the sprintstart chat CLI AI
9 Wiki#320 Docs: one conversation engine Docs
— sprintstart-frontend#236 Buddy composer lag (bug, kiranfin). Not blocked by anything: fix any time Frontend

User Story

As a new developer or team member,
I want one AI assistant surface where I can ask codebase and documentation questions, filter by source and date, browse my named past conversations, and get onboarding guidance with actionable tools,
so that I do not have to switch between two redundant interfaces (/chat and /buddy) or lose my history, filters and quoting.


Decision (2026-09-24)

/chat is retired. Buddy is the only conversation surface. Every chat-only capability moves onto Buddy first; then the chat surface goes, and the chat module code is removed once nothing calls it.

Why now: PR sprintstart-frontend#209 (Sep 3) put both pages under AssistantShell with a tab switch as an interim step, and sprintstart-frontend#206 was closed on Sep 9 before its acceptance criteria were met. Since then both engines have kept growing apart (chat: #227 replies, #230 queue, #225 filter popover, backend#236 binning; buddy: board actions, tutor #311, PM team mode #312). Every sprint we wait adds more to port.


Decisions taken (binding for all sub-issues)

  1. Project per conversation. Each named Buddy conversation carries an optional active projectId, set the way chat sets it today. With a project, retrieval is scoped to it and the question feeds that project's FAQ / Knowledge Gaps. With no project, retrieval spans all of the hire's projects (today's BuddyService.projectIdsFor) and no FAQ event is published, because the question belongs to no single project's FAQ.
  2. History moves over with its ids. A one-shot, idempotent Kotlin backfill (ApplicationRunner, keyed on the source id so a re-run is a no-op) copies chats/chat_messages into Buddy conversations and reuses the chat UUIDs as conversation ids. /chat/:id and board cards created by chatLink() then redirect to /buddy/:id without a lookup table. Each hire's current rolling visit becomes one conversation. Ships in sprintstart-backend#259, after its citation/bin/isIncomplete schema exists (an idempotent run before that would strand them).
  3. Search-only mode gets real chat parity. Capabilities-off Buddy gets the chat agent's grep tool next to search_docs, honours the source/date filters, and returns the model's reasoning so the reasoning/tool panel moves over. Covered by sprintstart-ai#206.
  4. PM team mode (BuddyTeamSession) is out of scope. It is a separate entity with its own compaction; multi-session applies to the hire's personal Buddy only.
  5. A new conversation is a new named session. Visit dividers, the fresh-visit semantics and "Clear the earlier conversation" go away. The greeting opens only the hire's first conversation (and one after a long absence); later conversations start empty, like chat. (2026-09-25)
  6. Admin chat endpoints are dropped, not ported: nobody reads a hire's private conversations. (2026-09-25)
  7. Not ported from chat: KaTeX math, ChatPage's aria-live turn announcements, the chat mascot idle animations (SleepyBot, BotGlyph gaze). Ported: conversation search + date grouping in the rail. (2026-09-25)
  8. The dead code goes too: the backend chat module and AI /api/v1/chat get their own cleanup issues, blocked by the frontend switch. (2026-09-25)
  9. Reasoning is batch, not live. The AI agent endpoint stays synchronous JSON; BuddyAgentResponse.reasoning is emitted by the backend as reasoning events before the answer tokens. True SSE streaming of the agent turn is out of scope. (2026-09-25)
  10. Conversation history takes BuddyPage's left rail; PM Replies move into a SidePanel drawer opened from a header button with an unread badge. BuddyPage's "No project yet" gate goes away, so a hire without a project gets an unscoped conversation. (2026-09-25)
  11. The dock shows the most recently active conversation; "New conversation" there creates one; expanding goes to that conversation's /buddy/:id. (2026-09-25)
  12. FAQ input: every question in a project-scoped conversation, in both modes, with quoted selections stripped. The AI classifier drops action/mentor requests (sprintstart-ai#209). (2026-09-25)
  13. Buddy gets chat's safety and evidence rules: the prompt-injection fence around the question, chat's evidence budget for search-only, and chat's grounding prompt (sprintstart-ai#206). (2026-09-25)
  14. Deep Thinking is not part of this. The local feature/deep-thinking-mode branches (never pushed) are dropped. (2026-09-25)

Verified state on dev (2026-09-24, extended 2026-09-25)

Capability /chat /buddy on dev Issue
Capabilities off = search only n/a Backend + AI done (backend#206) FE control: frontend#206
Source/date filters ChatFilters → AI RetrievalFilters none ai#206, backend#214, frontend#206
Named sessions, titles /api/v1/generate-title one rolling visit per user (uq_buddy_sessions_user) backend#214, frontend#206
grep tool, reasoning ChatAgent (GrepTool, ReasoningDelta) search_docs only, no reasoning ai#206, frontend#266
FAQ / Knowledge Gaps input ChatQuestionAskedEvent + ChatQuestionApi not published backend#214
Citations persisted, popover + artifact drawer not persisted, not rendered (only collected in the hook) backend#259, frontend#266
Delete message DELETE /chats/messages/me/{id} none backend#259
Bin + scheduled cleanup backend#236 none backend#259, frontend#266
Stop, queue while streaming stopStreaming, QueuedMessages (#230) none frontend#266
Rail search + date grouping ChatSidebar no rail frontend#266
Quote into prompt reply to message (#227) selection quote shipped (PR #262) frontend#206 (parity check)
Copy, save to board yes yes —

Architectural seams

Backend (sprintstart-backend)

  • There is no Flyway. build.gradle.kts has no Flyway dependency, the schema comes from ddl-auto: update, and the db/migration/V*.sql files never run (V10 and V11 each exist several times, which Flyway would reject). An earlier draft of this story assumed a Flyway migration; that path does not exist.
  • ddl-auto: update never drops constraints. Removing the @UniqueConstraint from BuddySession leaves uq_buddy_sessions_user in every existing database. The backfill runner (decision 2) drops it explicitly (ALTER TABLE buddy_sessions DROP CONSTRAINT IF EXISTS uq_buddy_sessions_user) before creating a second conversation.
  • BuddySession gains title, optional projectId, status/binnedAt (binning semantics copied from chat); BuddyService.getOrCreateSession(userId) becomes per-conversation; BuddyCompactionService.compactIfNeeded(userId) becomes per conversation id.
  • New endpoints under /api/v1/onboarding/me/buddy: list / create / rename / bin conversations; messages and stream take a conversation id; titles via the existing AI /api/v1/generate-title.
  • Insights coupling. insights reads questions only from the chat module: ChatQuestionEventListener + FaqLiveUpdateService (live) and InsightsFaqService via ChatQuestionApi (rebuild and counts). Buddy must publish a question event for project-scoped conversations and provide the rebuild source, or FAQ rebuilds silently shrink to old chat questions. Suggested: a module-neutral QuestionAskedEvent / QuestionSource in insights.external that both chat (until removed) and Buddy implement, instead of Buddy depending on chat.external.
  • Filters: extend SendBuddyMessageRequest + BuddyAgentDtos with the same shape as ChatFilters (sourceSystems, from, to) and pass them to the AI.
  • The stream mirrors AiStreamMessage; emit BuddyAgentResponse.reasoning as reasoning events before the answer tokens (emitAgentReply). The agent endpoint is synchronous JSON and BuddyReplyStream paces a finished answer, so reasoning is batch, not live.

AI (sprintstart-ai) — sprintstart-ai#206

  • RetrievalFilters (rag/types.py) already has source_systems, time_from, time_to, and the ChatFilters schema (api/schemas.py) already validates them. Reuse both; do not add a parallel schema.
  • buddy_agent.run_agent_turn hard-codes RetrievalFilters(project_ids=project_ids): merge the request filters in, and apply them to grep as well.
  • Capabilities off: mount GrepTool beside search_docs; emit reasoning deltas the way ChatAgent does; return chat's canned "no matching sources for the selected filters" reply when the filters leave nothing (_has_narrowing_filters in routes/chat.py).
  • Cleanup: retire /api/v1/chat, ChatAgent and ChatOrchestrator in sprintstart-ai#207, after the backend chat module is gone (sprintstart-backend#260).

Frontend (sprintstart-frontend) — sprintstart-frontend#206

  • Routing: drop the AssistantShell tab bar; /chat → /buddy, /chat/:id → /buddy/:id (AppRouter.tsx, accessPolicy.ts incl. the /chat/ prefix map, routePrefetch.ts, PageTransition.tsx, assistantSurfaces.ts, AssistantSurfaceSwitch.tsx, SideBar.tsx).
  • Port into features/buddy: conversation rail (from ChatSidebar), filter popover ([Feature]: Backend communication to Keycloak for user management #225), queue ([Feature]: Question UI #230), capabilities control, reasoning panel, citations with artifact drawer (details in sprintstart-frontend#266).
  • Consumers outside features/chatbot that must be re-pointed before deletion: QuickChatWidget, RecentChatsWidget, hooks/useAskAi.ts, board/generation/chatToCard.ts (chatLink), board/selection/SelectionActions.tsx (reads ChatContext.quoteSelection), DinoGameModal and GenerationScreen (import chatbot/components/DinoGame), App.tsx (ChatProvider).
  • Then delete features/chatbot/, pages/ChatPage.tsx, context/ChatProvider.tsx/ChatContext.ts, services/chatService.ts, and their tests.

Open PRs that touch this (2026-09-24)

PR Overlap Recommendation
frontend#265 (tutor #311, draft) BuddyComposer, useBuddyConversation, buddyService, ChatComposer Merge before the FE multi-session work starts; that work rewrites the same hook
frontend#184 (easter eggs) ChatPage, useChat, ChatProvider, DinoGame, chat tests, Buddy components Merge first; the retirement then moves DinoGame out of features/chatbot instead of losing it
frontend#264 (KB) SideBar.tsx only Trivial conflict
backend#254–#257 (team mode) BuddyTeamService / BuddyTeamSession, V20__…sql No conflict with personal sessions (decision 4); V20 is another never-executed script, see the Flyway note
backend#258, ai#204 (KB) api/schemas.py Trivial conflict
ai#208 (tutor #311, opened 2026-09-25) buddy_agent.py, buddy_persona.py (+268) Merge before sprintstart-ai#206; that issue edits the same persona and agent loop

Order of work

  1. AI: filters + grep + reasoning in the buddy turn (sprintstart-ai#206).
  2. Backend: conversations + filters + reasoning events + insights source (sprintstart-backend#214), then citations, message delete, binning, the chat history backfill, conversations replace visits (sprintstart-backend#259).
  3. Frontend: parity inside Buddy (sprintstart-frontend#266), then one surface, redirects and chat removal (sprintstart-frontend#206).
  4. Demo seed on the cluster moves to Buddy (uni-cluster#10), so nothing outside the apps still calls chat.
  5. Cleanup: backend chat module (sprintstart-backend#260), then AI /api/v1/chat and the sprintstart chat CLI (sprintstart-ai#207).
  6. Docs (Wiki#320).

Independent of the order: sprintstart-frontend#236 (composer lag) is a standalone bug and should be fixed now.


Acceptance Criteria

  • /chat, /chat/:id and existing board-card chat links land on /buddy / /buddy/:id; no 404s
  • A hire can hold several named Buddy conversations, see them in a side rail, rename and bin them
  • Old chat transcripts and the current rolling visit appear as conversations, transcripts intact, chat ids preserved
  • The capabilities control is visible and its state obvious. On: all tools (board, onboarding path). Off: no action tools, search_docs + grep only, answers with citations
  • Source-system (GitHub, Jira, Confluence, Uploads) and date filters restrict retrieval, in both modes
  • Reasoning / tool activity is visible in Buddy (after the model thought, decision 9)
  • A hire without a project can use Buddy (unscoped conversation); PM Replies open from the header drawer
  • Buddy replies show citations (popover + artifact drawer) live and after reload
  • Stop, message deletion and conversation binning with scheduled cleanup work in Buddy
  • Questions in a project-scoped conversation update that project's FAQ and Knowledge Gaps, live and on rebuild; questions without a project do not
  • Quoting and queueing while streaming work in Buddy
  • No references to features/chatbot, ChatProvider or chatService remain; npm run try passes
  • ./gradlew build passes
  • AI gates pass: ruff format --check, ruff check, pyright src/, pytest

Out of Scope

  • The floating Buddy dock and its expand handoff (it must keep landing on the unified page, but is not redesigned)
  • PM team mode sessions (decision 4)
  • Cross-conversation search beyond the rail's title search
  • Admin access to conversations (decision 6)
  • KaTeX, aria-live announcements, chat mascot animations (decision 7)

Sub-Tasks

Related issues affected by the retirement

Context

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requeststoryteam:aiAI / Retrieval team (sprintstart-ai, Python)team:backendBackend team (sprintstart-backend, Kotlin/Spring Boot)team:devopsDevOps / CI / deployment (cross-repo)team:docsDocs / Knowledge (Wiki, readthedocs)team:frontendFrontend team (sprintstart-frontend, React)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions