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
Paired with sprintstart-frontend#206, which retires the chat surface.
Summary & Goal
Move the two things the chat has and the buddy does not onto the buddy, so one engine can serve
both uses and /chat can be retired.
Technical Specification
The overlap is already almost total, by the code's own account.
onboarding/external/model/BuddyStreamEvent.kt says it "mirrors sprintstart-backend's own chat module's AiStreamMessage shape field-for-field, using the same sse_event vocabulary /chat does (tool_use/token/citation/action_proposal/done/error)". Retrieval already
runs AI-side on the buddy agent endpoint — search_docs, per BuddyAgentDtos.kt — and project
scope is already carried, via BuddyService.projectIdsFor (:352). Citations already stream.
What is genuinely chat-only
Filters.chat/models/ChatFilters.kt — sourceSystems: List<SourceSystem>, from, to.
Nothing equivalent exists on the buddy path.
Multiple named sessions. The buddy has exactly one rolling visit per hire
(BuddyService.getOrCreateSession). The chat has real sessions with AI-generated titles via /api/v1/generate-title.
Changes
Carry retrieval filters through the buddy agent request to the AI service.
Let a hire hold more than one buddy conversation, with titles. This is the larger half: BuddySession is built around one rolling visit per hire, and BuddyCompactionService folds it.
Compaction must stay per conversation.
Preserve existing transcripts. A hire's current rolling visit becomes one conversation, not a
discarded one.
Keep the buddy's own behaviour intact — tools, action proposals and board writes still apply
where capabilities are on, and are absent where they are off.
Out of scope: deleting the chat module. Retire the surface first, remove the code once
nothing calls it.
Acceptance Criteria
Source-system and date filters apply to buddy retrieval
A hire can hold several named buddy conversations
Titles are generated as they are for chats today
Compaction operates per conversation
Existing rolling visits survive as conversations, with transcripts intact
Capabilities-off mode still behaves as it does today
Unit tests cover filtered retrieval, multi-conversation isolation, titling, and migration of an existing visit
This is step 2 of Wiki#319. Blocked by sprintstart-ai#206; blocks sprintstart-backend#259 and sprintstart-frontend#266/#206. The text above still stands; these points refine it, verified against dev.
No Flyway. There is no dependency; the schema comes from ddl-auto: update, and db/migration/V*.sql never runs. ddl-auto never drops constraints, so uq_buddy_sessions_user has to be dropped explicitly (a startup step, idempotent) before a hire can hold a second conversation.
Project per conversation (decision 1). Each conversation has an optional projectId. Set → retrieval uses that project and the FAQ is fed; unset → all of the hire's projects (projectIdsFor), no FAQ event.
FAQ source.insights reads questions only through chat today: the event (ChatQuestionEventListener / FaqLiveUpdateService) and ChatQuestionApi (InsightsFaqService rebuilds and counts). Add a neutral question event and source in insights.external and publish from Buddy, or rebuilds quietly shrink to old chat questions.
Filters. Same shape as ChatFilters, passed on in BuddyAgentRequest.filters (ai#206).
Reasoning (batch, decided). The agent endpoint stays synchronous JSON. Read BuddyAgentResponse.reasoning from each agent call of the tool loop and emit it as reasoning events before the answer tokens in emitAgentReply (BuddyReplyStream.kt). Never persisted.
Titles. Reuse /api/v1/generate-title, as chat does, with the conversation's first user message, never Buddy's opening greeting.
What feeds the FAQ (decided). Every question in a project-scoped conversation, in both capability modes. Filtering out action and mentor requests is the AI classifier's job (sprintstart-ai#209, which blocks this issue). Before publishing, strip quoted selections (> … blocks from quoteFromSelection) so only the hire's own words are grouped. Team-mode questions never feed it.
Updated criteria: the acceptance criterion "Existing rolling visits survive as conversations" and the unit test "migration of an existing visit" now belong to #259. Added here:
uq_buddy_sessions_user is dropped on existing databases, not only fresh ones
A project-scoped question updates that project's FAQ live and on rebuild; an unscoped one does not
Published question text has quoted selections removed
Titles come from the first user message
reasoning events arrive before the answer tokens and are not persisted
Dependencies
#206, the capabilities-off buddy mode.sprintstart-frontend#206, which retires the chat surface.Summary & Goal
Move the two things the chat has and the buddy does not onto the buddy, so one engine can serve
both uses and
/chatcan be retired.Technical Specification
The overlap is already almost total, by the code's own account.
onboarding/external/model/BuddyStreamEvent.ktsays it "mirrorssprintstart-backend's ownchatmodule'sAiStreamMessageshape field-for-field, using the samesse_eventvocabulary/chatdoes (tool_use/token/citation/action_proposal/done/error)". Retrieval alreadyruns AI-side on the buddy agent endpoint —
search_docs, perBuddyAgentDtos.kt— and projectscope is already carried, via
BuddyService.projectIdsFor(:352). Citations already stream.What is genuinely chat-only
chat/models/ChatFilters.kt—sourceSystems: List<SourceSystem>,from,to.Nothing equivalent exists on the buddy path.
(
BuddyService.getOrCreateSession). The chat has real sessions with AI-generated titles via/api/v1/generate-title.Changes
BuddySessionis built around one rolling visit per hire, andBuddyCompactionServicefolds it.Compaction must stay per conversation.
discarded one.
where capabilities are on, and are absent where they are off.
Out of scope: deleting the
chatmodule. Retire the surface first, remove the code oncenothing calls it.
Acceptance Criteria
Definition of Done
./gradlew checkpassesdevUpdated 2026-09-25 (chat → Buddy migration, SprintStartProject/Wiki#319)
This is step 2 of Wiki#319. Blocked by sprintstart-ai#206; blocks sprintstart-backend#259 and sprintstart-frontend#266/#206. The text above still stands; these points refine it, verified against
dev.ddl-auto: update, anddb/migration/V*.sqlnever runs.ddl-autonever drops constraints, souq_buddy_sessions_userhas to be dropped explicitly (a startup step, idempotent) before a hire can hold a second conversation.projectId. Set → retrieval uses that project and the FAQ is fed; unset → all of the hire's projects (projectIdsFor), no FAQ event.insightsreads questions only through chat today: the event (ChatQuestionEventListener/FaqLiveUpdateService) andChatQuestionApi(InsightsFaqServicerebuilds and counts). Add a neutral question event and source ininsights.externaland publish from Buddy, or rebuilds quietly shrink to old chat questions.ChatFilters, passed on inBuddyAgentRequest.filters(ai#206).BuddyAgentResponse.reasoningfrom each agent call of the tool loop and emit it asreasoningevents before the answer tokens inemitAgentReply(BuddyReplyStream.kt). Never persisted./api/v1/generate-title, as chat does, with the conversation's first user message, never Buddy's opening greeting.> …blocks fromquoteFromSelection) so only the hire's own words are grouped. Team-mode questions never feed it.isIncompleteschema (running it here would strand them for good). So does "a hire's current rolling visit becomes one conversation". Removing thechatmodule is Remove the chat module once Buddy has replaced it #260.BuddyTeamSession), so no conflict with Let the team-mode buddy manage who is on a project and what they do #254–Keep a team-mode area open for the manager's next message #257.Updated criteria: the acceptance criterion "Existing rolling visits survive as conversations" and the unit test "migration of an existing visit" now belong to #259. Added here:
uq_buddy_sessions_useris dropped on existing databases, not only fresh onesreasoningevents arrive before the answer tokens and are not persisted