Skip to content

Move the chat's filters and named sessions onto the buddy #214

Description

@LinseCed

Dependencies

  • Builds on #206, the capabilities-off buddy mode.
  • 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

  1. Filters. chat/models/ChatFilters.kt — sourceSystems: List<SourceSystem>, from, to.
    Nothing equivalent exists on the buddy path.
  2. 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

Definition of Done

  • ./gradlew check passes
  • PR reviewed and merged into dev

Updated 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.

  • 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.
  • Moved out: the chat history backfill goes to Buddy parity with chat: persisted citations, message deletion, binning, conversations replace visits #259, because it needs Buddy parity with chat: persisted citations, message deletion, binning, conversations replace visits #259's citation, bin and isIncomplete schema (running it here would strand them for good). So does "a hire's current rolling visit becomes one conversation". Removing the chat module is Remove the chat module once Buddy has replaced it #260.
  • Out of scope: team mode (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_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

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions