You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
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)
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.
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).
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.
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.
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)
Admin chat endpoints are dropped, not ported: nobody reads a hire's private conversations. (2026-09-25)
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)
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)
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)
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)
The dock shows the most recently active conversation; "New conversation" there creates one; expanding goes to that conversation's /buddy/:id. (2026-09-25)
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)
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)
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)
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).
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.
Merge before sprintstart-ai#206; that issue edits the same persona and agent loop
Order of work
AI: filters + grep + reasoning in the buddy turn (sprintstart-ai#206).
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).
Frontend: parity inside Buddy (sprintstart-frontend#266), then one surface, redirects and chat removal (sprintstart-frontend#206).
Demo seed on the cluster moves to Buddy (uni-cluster#10), so nothing outside the apps still calls chat.
Cleanup: backend chat module (sprintstart-backend#260), then AI /api/v1/chat and the sprintstart chat CLI (sprintstart-ai#207).
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
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 (
/chatand/buddy) or lose my history, filters and quoting.Decision (2026-09-24)
/chatis 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
AssistantShellwith 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)
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'sBuddyService.projectIdsFor) and no FAQ event is published, because the question belongs to no single project's FAQ.ApplicationRunner, keyed on the source id so a re-run is a no-op) copieschats/chat_messagesinto Buddy conversations and reuses the chat UUIDs as conversation ids./chat/:idand board cards created bychatLink()then redirect to/buddy/:idwithout a lookup table. Each hire's current rolling visit becomes one conversation. Ships in sprintstart-backend#259, after its citation/bin/isIncompleteschema exists (an idempotent run before that would strand them).greptool next tosearch_docs, honours the source/date filters, and returns the model's reasoning so the reasoning/tool panel moves over. Covered by sprintstart-ai#206.BuddyTeamSession) is out of scope. It is a separate entity with its own compaction; multi-session applies to the hire's personal Buddy only.ChatPage'saria-liveturn announcements, the chat mascot idle animations (SleepyBot,BotGlyphgaze). Ported: conversation search + date grouping in the rail. (2026-09-25)chatmodule and AI/api/v1/chatget their own cleanup issues, blocked by the frontend switch. (2026-09-25)BuddyAgentResponse.reasoningis emitted by the backend asreasoningevents before the answer tokens. True SSE streaming of the agent turn is out of scope. (2026-09-25)/buddy/:id. (2026-09-25)feature/deep-thinking-modebranches (never pushed) are dropped. (2026-09-25)Verified state on
dev(2026-09-24, extended 2026-09-25)/chat/buddyon devChatFilters→ AIRetrievalFilters/api/v1/generate-titleuq_buddy_sessions_user)greptool, reasoningChatAgent(GrepTool,ReasoningDelta)search_docsonly, no reasoningChatQuestionAskedEvent+ChatQuestionApiDELETE /chats/messages/me/{id}stopStreaming,QueuedMessages(#230)ChatSidebarArchitectural seams
Backend (
sprintstart-backend)build.gradle.ktshas no Flyway dependency, the schema comes fromddl-auto: update, and thedb/migration/V*.sqlfiles 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: updatenever drops constraints. Removing the@UniqueConstraintfromBuddySessionleavesuq_buddy_sessions_userin 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.BuddySessiongainstitle, optionalprojectId,status/binnedAt(binning semantics copied from chat);BuddyService.getOrCreateSession(userId)becomes per-conversation;BuddyCompactionService.compactIfNeeded(userId)becomes per conversation id./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.insightsreads questions only from the chat module:ChatQuestionEventListener+FaqLiveUpdateService(live) andInsightsFaqServiceviaChatQuestionApi(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-neutralQuestionAskedEvent/QuestionSourceininsights.externalthat both chat (until removed) and Buddy implement, instead of Buddy depending onchat.external.SendBuddyMessageRequest+BuddyAgentDtoswith the same shape asChatFilters(sourceSystems,from,to) and pass them to the AI.AiStreamMessage; emitBuddyAgentResponse.reasoningasreasoningevents before the answer tokens (emitAgentReply). The agent endpoint is synchronous JSON andBuddyReplyStreampaces a finished answer, so reasoning is batch, not live.AI (
sprintstart-ai) — sprintstart-ai#206RetrievalFilters(rag/types.py) already hassource_systems,time_from,time_to, and theChatFiltersschema (api/schemas.py) already validates them. Reuse both; do not add a parallel schema.buddy_agent.run_agent_turnhard-codesRetrievalFilters(project_ids=project_ids): merge the request filters in, and apply them togrepas well.GrepToolbesidesearch_docs; emit reasoning deltas the wayChatAgentdoes; return chat's canned "no matching sources for the selected filters" reply when the filters leave nothing (_has_narrowing_filtersinroutes/chat.py)./api/v1/chat,ChatAgentandChatOrchestratorin sprintstart-ai#207, after the backend chat module is gone (sprintstart-backend#260).Frontend (
sprintstart-frontend) — sprintstart-frontend#206AssistantShelltab bar;/chat→/buddy,/chat/:id→/buddy/:id(AppRouter.tsx,accessPolicy.tsincl. the/chat/prefix map,routePrefetch.ts,PageTransition.tsx,assistantSurfaces.ts,AssistantSurfaceSwitch.tsx,SideBar.tsx).features/buddy: conversation rail (fromChatSidebar), 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).features/chatbotthat must be re-pointed before deletion:QuickChatWidget,RecentChatsWidget,hooks/useAskAi.ts,board/generation/chatToCard.ts(chatLink),board/selection/SelectionActions.tsx(readsChatContext.quoteSelection),DinoGameModalandGenerationScreen(importchatbot/components/DinoGame),App.tsx(ChatProvider).features/chatbot/,pages/ChatPage.tsx,context/ChatProvider.tsx/ChatContext.ts,services/chatService.ts, and their tests.Open PRs that touch this (2026-09-24)
BuddyComposer,useBuddyConversation,buddyService,ChatComposerChatPage,useChat,ChatProvider,DinoGame, chat tests, Buddy componentsDinoGameout offeatures/chatbotinstead of losing itSideBar.tsxonlyBuddyTeamService/BuddyTeamSession,V20__…sqlV20is another never-executed script, see the Flyway noteapi/schemas.pybuddy_agent.py,buddy_persona.py(+268)Order of work
grep+ reasoning in the buddy turn (sprintstart-ai#206).chatmodule (sprintstart-backend#260), then AI/api/v1/chatand thesprintstart chatCLI (sprintstart-ai#207).Independent of the order: sprintstart-frontend#236 (composer lag) is a standalone bug and should be fixed now.
Acceptance Criteria
/chat,/chat/:idand existing board-card chat links land on/buddy//buddy/:id; no 404ssearch_docs+greponly, answers with citationsfeatures/chatbot,ChatProviderorchatServiceremain;npm run trypasses./gradlew buildpassesruff format --check,ruff check,pyright src/,pytestOut of Scope
aria-liveannouncements, chat mascot animations (decision 7)Sub-Tasks
grepand reasoning in the buddy turn — [Feature]: Buddy search parity: source/date filters, grep tool and streamed reasoning sprintstart-ai#206isIncomplete, binning + cleanup, conversations replace visits, chat history backfill — Buddy parity with chat: persisted citations, message deletion, binning, conversations replace visits sprintstart-backend#259chatmodule — Remove the chat module once Buddy has replaced it sprintstart-backend#260/api/v1/chatandChatAgent— Remove /api/v1/chat and the chat agent once the backend chat module is gone sprintstart-ai#207Related issues affected by the retirement
features/chatbotcomponents that this story deletesContext
team:backend,team:frontend,team:ai