Files
resolutionflow/docs/superpowers/specs/2026-03-24-conversational-branching-design.md
chihlasm a9b2aadb9e docs: add Conversational Branching design spec
Full design for branching troubleshooting workspace — extends existing
infrastructure without replacing it. Additive tables, nullable columns,
dual-write backward compat. Removable if feature is pulled.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 07:41:25 +00:00

25 KiB

Conversational Branching — Design Spec

Date: 2026-03-24 Status: Draft Branch: feat/conversational-branching (to be created) Source: docs/ConversationalBranching_DataModel_Spec.docx (original spec)


Executive Summary

Conversational Branching transforms FlowPilot from a linear AI chat into a branching troubleshooting workspace. Engineers explore multiple diagnostic hypotheses as first-class branches, with full AI context awareness across all paths.

The design is additive and removable — all branching state lives in new tables and nullable columns. If the feature is pulled, drop the tables, remove the columns, and the app works exactly as it does today.


Design Principles

  1. Extend, don't replace. Reuse existing tables, services, and infrastructure. No parallel code paths.
  2. Removable. Every branching artifact (tables, columns, services, components) can be deleted without breaking existing functionality.
  3. Dual-write for backward compat. New handoff system writes to both session_handoffs table AND existing escalation_package/escalated_to_id fields until the old queue UI is fully migrated.
  4. Branching services never touch the Anthropic SDK. They assemble context and call _call_ai from assistant_chat_service.py — the same function unified_chat_service already uses.

What This Enables

  1. Branch Map sidebar — live tree visualization of all diagnostic paths, with status badges (active, dead end, solved, untried) and click-to-switch.
  2. Fork Cards — in-chat decision points where FlowPilot suggests multiple hypotheses, each becoming a branch.
  3. Cross-branch AI context — when exploring Branch 3, FlowPilot knows what was tried on Branches 1 and 2.
  4. Branch revival — new evidence from one branch can reopen a previously dead-end branch.
  5. Unified handoff (park / escalate) — single snapshot mechanism with intent toggle.
  6. Three-output resolution package — PSA ticket notes, KB article draft, and client-facing summary.

Reuse Map

What branching reuses (NOT duplicated):

Capability Existing Source How Branching Uses It
LLM calls _call_ai in assistant_chat_service.py All branching LLM calls go through this
Image content blocks _call_ai multimodal support Current branch images included in messages via existing format
Token counting AISessionStep.input_tokens/output_tokens Unchanged — tokens tracked per step per branch
RAG search rag_service.py Branch messages use same RAG pipeline
Image upload + S3 storage file_uploads table + storage_service.py Extended with 5 new columns, no new table
PSA push psa_documentation_service.py Resolution outputs and handoff notes push through existing service
Escalation package _build_escalation_package_enhanced() HandoffManager reuses this for snapshot generation
Escalation queue Existing ai_sessions query + frontend Dual-write keeps old queue working
Session lifecycle flowpilot_engine.py resolve/escalate/pause Extended, not replaced

What's new:

Capability Notes
session_branches table Core branching entity
fork_points table Decision point metadata (kept separate for expandability)
session_handoffs table Unified park/escalate with history
session_resolution_outputs table Three independent output deliverables
BranchManager service Branch lifecycle CRUD + context summary generation
BranchAwarePromptBuilder service Cross-branch context assembly
HandoffManager service Snapshot, assessment, claim, PSA push
ResolutionOutputGenerator service PSA notes, KB article, client summary generation
AI description pipeline Async ai_description generation on every file upload

Data Model

Modified existing tables

ai_sessions — add columns:

Column Type Default Notes
is_branching BOOLEAN FALSE Whether branching is active
active_branch_id UUID FK NULLABLE NULL Currently viewed branch
handoff_count INTEGER 0 Times handed off
total_active_seconds INTEGER 0 Cumulative active time
total_parked_seconds INTEGER 0 Cumulative parked time

ai_session_steps — add columns:

Column Type Default Notes
branch_id UUID FK NULLABLE NULL NULL = pre-branching/root
is_fork_point BOOLEAN FALSE Whether this step triggered a fork
fork_point_id UUID FK NULLABLE NULL References fork_points.id

file_uploads — add columns:

Column Type Default Notes
ai_description TEXT NULLABLE NULL AI-generated one-sentence description
extracted_content TEXT NULLABLE NULL Extracted text from logs/configs
content_summary TEXT NULLABLE NULL AI summary for long files
uploaded_on_branch_id UUID FK NULLABLE NULL Which branch the file was uploaded from
uploaded_at_step_id UUID FK NULLABLE NULL Which step triggered the upload

All columns nullable. Existing rows unaffected. ai_description is always generated on upload (not just branching sessions) — useful for search, exports, PSA notes, Knowledge Flywheel.

Also add 'fork' to ai_session_steps.step_type check constraint.

New tables

session_branches

Column Type Notes
id UUID PK
session_id UUID FK → ai_sessions.id CASCADE
parent_branch_id UUID FK → self, NULLABLE NULL = root branch
fork_point_step_id UUID FK → ai_session_steps.id, NULLABLE Step where this branch forked
branch_order INTEGER Display order among siblings (1-based)
label VARCHAR(200) "Network connectivity", "Print spooler service"
status VARCHAR(20) active, dead_end, solved, untried, revived
status_reason TEXT NULLABLE AI-generated reason for status
status_changed_at TIMESTAMP NULLABLE
status_changed_by UUID FK NULLABLE
conversation_messages JSONB LLM message history scoped to this branch
context_summary JSONB {tried: [], concluded: str, artifacts: []}
evidence_from_branch_id UUID FK NULLABLE If revived, evidence source
evidence_description TEXT NULLABLE What triggered revival
created_at TIMESTAMP
updated_at TIMESTAMP

Indexes: session_id, parent_branch_id, (session_id, status), (session_id, branch_order). Check constraints: status IN (...), branch_order > 0.

fork_points

Column Type Notes
id UUID PK
session_id UUID FK → ai_sessions.id
parent_branch_id UUID FK → session_branches.id Branch this fork occurs in
trigger_step_id UUID FK → ai_session_steps.id, NULLABLE Step that triggered fork
fork_reason TEXT AI explanation
options JSONB [{label, description, branch_id, status}]
created_at TIMESTAMP

session_handoffs

Column Type Notes
id UUID PK
session_id UUID FK → ai_sessions.id
handed_off_by UUID FK → users.id
intent VARCHAR(20) park or escalate
source_branch_id UUID FK NULLABLE Active branch at handoff
snapshot JSONB Branch map, status, next step, waiting on, watch out
ai_assessment TEXT NULLABLE Diagnostic assessment (escalate only)
ai_assessment_data JSONB NULLABLE {likely_cause, suggested_steps, confidence}
artifacts JSONB NULLABLE [{name, type, reference}]
engineer_notes TEXT NULLABLE
priority VARCHAR(20) normal or elevated
claimed_by UUID FK NULLABLE
claimed_at TIMESTAMP NULLABLE
psa_note_pushed BOOLEAN DEFAULT FALSE
psa_note_id VARCHAR(100) NULLABLE
notification_sent BOOLEAN DEFAULT FALSE
created_at TIMESTAMP

Check constraints: intent IN ('park', 'escalate'), priority IN ('normal', 'elevated'). Dual-write: on create, also populates ai_sessions.escalation_package and escalated_to_id.

session_resolution_outputs

Column Type Notes
id UUID PK
session_id UUID FK → ai_sessions.id
output_type VARCHAR(30) psa_ticket_notes, knowledge_base, client_summary
generated_content TEXT AI-generated output
structured_data JSONB NULLABLE For KB: {symptoms, root_cause, steps, tags}
edited_content TEXT NULLABLE Engineer's edited version
status VARCHAR(20) draft, approved, pushed, rejected
pushed_to VARCHAR(50) NULLABLE psa, kb_library, clipboard, email
pushed_at TIMESTAMP NULLABLE
pushed_reference VARCHAR(200) NULLABLE External ID after push
generated_by_model VARCHAR(50)
created_at TIMESTAMP
updated_at TIMESTAMP

Constraints: UNIQUE(session_id, output_type), check constraints on output_type and status.

Entity Relationships

AISession 1──* SessionBranch (session has many branches)
AISession 1──* AISessionStep (unchanged)
AISession 1──* SessionHandoff (can be handed off multiple times)
AISession 1──3 SessionResolutionOutput (one per output type)
SessionBranch 1──* AISessionStep (branch owns steps via branch_id)
SessionBranch 1──* SessionBranch (parent → children, self-referential)
SessionBranch 1──* ForkPoint (branch contains fork points)
ForkPoint 1──* SessionBranch (each option becomes a branch)
SessionHandoff *──1 User (handed_off_by, claimed_by)
FileUpload *──1 SessionBranch (optional, via uploaded_on_branch_id)

Service Layer

Integration Architecture

┌─────────────────────────────────────────────────────────────┐
│  LAYER 3: BRANCHING SERVICES (NEW)                          │
│                                                             │
│  BranchManager         — Fork, switch, mark status, revive, │
│                          tree query, context summary gen     │
│                                                             │
│  BranchAwarePromptBuilder — Assembles system prompt +       │
│                              messages + images with cross-   │
│                              branch context, returns dict    │
│                              for _call_ai                    │
│                                                             │
│  HandoffManager        — Snapshot, AI assessment, claim,     │
│                          briefing, PSA push, queue query     │
│                                                             │
│  ResolutionOutputGen   — PSA notes, KB article, client       │
│                          summary, push to destination        │
└────────────────────────────┬────────────────────────────────┘
                             │ calls _call_ai directly
                             ▼
┌─────────────────────────────────────────────────────────────┐
│  LAYER 2: EXISTING CHAT INFRASTRUCTURE                      │
│                                                             │
│  _call_ai / _call_anthropic_cached                          │
│    — Anthropic API, prompt caching, image blocks, MCP       │
│                                                             │
│  unified_chat_service  — Session-level chat (linear)        │
│  flowpilot_engine      — Step lifecycle, resolve, escalate  │
│  rag_service           — Flow library search                │
│  psa_documentation_service — ConnectWise note push          │
└────────────────────────────┬────────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────────┐
│  LAYER 1: INFRASTRUCTURE (EXISTS)                           │
│                                                             │
│  Railway Object Storage — S3 bucket, presigned URLs         │
│  PostgreSQL             — All data                          │
│  Anthropic API          — Claude with Vision                │
└─────────────────────────────────────────────────────────────┘

services/branch_manager.py

Branch lifecycle management. Pure data operations + one LLM call pattern (context summary).

Method What it does LLM call?
create_root_branch(session_id) Creates root branch, sets is_branching=True, copies session.conversation_messages into root branch. Session-level field kept as pre-branching snapshot. No
create_fork(session_id, parent_branch_id, trigger_step_id, fork_reason, options[]) Creates ForkPoint + N SessionBranch rows. Sets is_fork_point=True on trigger step. Unexplored options get status untried. No
switch_branch(session_id, target_branch_id) Updates session.active_branch_id. Returns branch with context. No
mark_branch_status(branch_id, status, reason) Updates status. Generates context_summary via _call_ai. Yes — summary
revive_branch(branch_id, evidence_from_branch_id, evidence_description) Sets status revived, records evidence source, prepends revival context to branch messages. No
get_branch_tree(session_id) Full tree with status, labels, step counts, summaries. No
build_cross_branch_context(branch_id) Reads context_summary from all sibling branches, returns formatted text. No

services/branch_aware_prompt_builder.py

Pure function — takes data, returns assembled prompt components. No DB access, no LLM calls.

Single method: build(branch, sibling_summaries, session_context, attachments, token_budget)

Returns: {system_prompt: str, history: list[dict], new_message: str, images: list[dict]}

Preserves _call_ai's cache breakpoint behavior by separating history from new message.

Assembly order:

  1. Session context (~2,000 tokens) — problem summary, domain, client info, PSA data
  2. Cross-branch summaries (~3,000 token cap) — prioritized: active > untried > revived > dead_end
  3. Revival context — if branch was revived, prepend evidence
  4. Attachment descriptions (~1,000 tokens) — ai_description from other branches' uploads
  5. Branch messages (remaining budget) — last 10-15 turns verbatim, older summarized
  6. Token budget enforcement — compress: old messages → dead-end summaries → file content → never drop system prompt, last 5 messages, branch status map

services/handoff_manager.py

Unified park/escalate with dual-write backward compatibility.

Method What it does LLM call?
create_handoff(session_id, intent, engineer_notes, user_id) Creates SessionHandoff. Calls generate_snapshot(). If escalate, calls generate_ai_assessment(). Dual-writes to session.escalation_package + escalated_to_id. Escalate only
generate_snapshot(session_id) Serializes branch tree into snapshot JSONB. Reuses _build_escalation_package_enhanced() for steps-tried data. No
generate_ai_assessment(session_id) Full session + branch context → diagnostic assessment. Yes
generate_briefing(handoff_id, claiming_user_id) Natural-language handoff summary for claiming engineer. Yes
claim_session(handoff_id, claiming_user_id) Updates claimed_by/at, sets session active. Dual-writes escalation_package. No
push_to_psa(handoff_id) Calls existing psa_documentation_service. No
get_queue(team_id, filters) DB query for parked + escalated sessions. No

services/resolution_output_generator.py

Three LLM calls on resolve, each through _call_ai.

Method What it does LLM call?
generate_all(session_id) Creates 3 SessionResolutionOutput rows. 3 calls
generate_psa_notes(session_id) Structured ticket notes with full branch history. Yes
generate_kb_article(session_id) KB draft — dead-end branches become "rule out first" guidance. structured_data has {symptoms, root_cause, steps, tags}. Yes
generate_client_summary(session_id) Non-technical summary for end user. Yes
push_output(output_id, destination) Routes: psapsa_documentation_service, kb_library → flow/step library, clipboard → returns content. No

AI Description Pipeline (upload extension)

Not a new service — extends the existing upload endpoint in uploads.py:

  1. Upload completes, response returned immediately.
  2. Background task (via asyncio.create_task) calls _call_ai with image + prompt: "Describe this in one sentence for a troubleshooting context log."
  3. Result written to file_uploads.ai_description.
  4. For text files: extract content directly, call _call_ai for summary if >2,000 tokens.
  5. Always runs (not just branching sessions) — useful for search, exports, PSA notes.

API Endpoints

All under existing /api prefix, JWT auth, team-scoped.

Branch Management

Method Endpoint Description
GET /ai-sessions/{id}/branches List all branches (tree structure)
POST /ai-sessions/{id}/branches/fork Create fork point with N branches
PATCH /ai-sessions/{id}/branches/{bid} Update branch status
POST /ai-sessions/{id}/branches/{bid}/switch Switch active branch
POST /ai-sessions/{id}/branches/{bid}/revive Revive dead-end with evidence
POST /ai-sessions/{id}/branches/{bid}/message Send message on a specific branch

Handoff (Park / Escalate)

Method Endpoint Description
POST /ai-sessions/{id}/handoff Create handoff (park or escalate)
GET /ai-sessions/{id}/handoffs Handoff history for session
POST /ai-sessions/{id}/handoffs/{hid}/claim Claim a handed-off session
GET /ai-sessions/queue Team queue (parked + escalated)

Resolution Outputs

Method Endpoint Description
POST /ai-sessions/{id}/resolve Resolve + auto-generate 3 outputs
GET /ai-sessions/{id}/outputs Get all resolution outputs
PATCH /ai-sessions/{id}/outputs/{oid} Edit output before pushing
POST /ai-sessions/{id}/outputs/{oid}/push Push to destination

Note: endpoints nest under /ai-sessions (not /api/v1/sessions as the original spec proposed) to match existing routing conventions.


Token Budget Strategy

Budget Allocation

Context Layer Budget Strategy
System prompt + session context ~2,000 tokens Fixed
Cross-branch summaries ~3,000 tokens Scales with branch count. Each summary ~200-500 tokens. Cap at 3,000.
Current branch messages Remaining budget Last 10-15 turns verbatim. Older summarized.
Attachment descriptions ~1,000 tokens Included in cross-branch summaries

Graceful Degradation (in order)

  1. Summarize old current-branch messages (keep last 8-10 verbatim)
  2. Trim cross-branch summaries (dead-end → one sentence)
  3. Drop file content, keep descriptions
  4. Never drop: system prompt, problem summary, last 5 messages, branch status map

Branch Limits by Plan

Plan Max Branches Rationale
Free 2 Experience branching, limit cost
Pro 5 Covers most scenarios
Team 10 Complex multi-path issues
Enterprise Unlimited

Frontend Components

Component Location Description
BranchMap components/session/BranchMap.tsx Sidebar tree visualization with status badges
BranchNode components/session/BranchNode.tsx Individual node in branch map
ForkCard components/session/ForkCard.tsx In-chat fork decision point
BranchTransitionBar components/session/BranchTransitionBar.tsx Context bar on branch switch
BranchRevivalCard components/session/BranchRevivalCard.tsx Evidence card for revival
HandoffModal components/session/HandoffModal.tsx Unified park/escalate modal
ResolutionOutputPanel components/session/ResolutionOutputPanel.tsx Three-tab resolution view
SessionQueuePage pages/SessionQueuePage.tsx Team queue for parked/escalated
Hook Location Description
useBranching hooks/useBranching.ts Branch state management
useHandoff hooks/useHandoff.ts Handoff flow state
useResolutionOutputs hooks/useResolutionOutputs.ts Resolution output state
API Client Location Description
branches.ts api/branches.ts Branch API client
handoffs.ts api/handoffs.ts Handoff API client
resolutions.ts api/resolutions.ts Resolution output API client

Backend File Locations

backend/app/
├── models/
│   ├── session_branch.py          # SessionBranch model
│   ├── fork_point.py              # ForkPoint model
│   ├── session_handoff.py         # SessionHandoff model
│   └── session_resolution_output.py  # SessionResolutionOutput model
├── schemas/
│   ├── session_branch.py          # Pydantic schemas
│   ├── session_handoff.py         # Pydantic schemas
│   └── session_resolution.py      # Pydantic schemas
├── services/
│   ├── branch_manager.py          # Branch lifecycle
│   ├── branch_aware_prompt_builder.py  # Cross-branch context assembly
│   ├── handoff_manager.py         # Park/escalate
│   └── resolution_output_generator.py  # 3-output generator
├── api/endpoints/
│   ├── session_branches.py        # Branch API router
│   ├── session_handoffs.py        # Handoff API router
│   └── session_resolutions.py     # Resolution output API router
└── alembic/versions/
    └── xxx_add_branching_tables.py  # Single migration

Implementation Phases

Phase 1: Data Foundation (est. 2 days)

  • Create 4 new models + Pydantic schemas
  • Add columns to ai_sessions, ai_session_steps, file_uploads
  • Single Alembic migration (all additive)
  • Unit tests for model creation and relationships

Phase 2: Branch Engine (est. 2-3 days)

  • BranchManager service
  • BranchAwarePromptBuilder service
  • Branch API endpoints
  • Integration with FlowPilot engine (branch_id on step creation)
  • Integration tests: create → fork → explore → dead-end → switch → verify cross-branch context

Phase 3: Handoff System (est. 1.5-2 days)

  • HandoffManager service with dual-write
  • Handoff API endpoints + queue endpoint
  • PSA push integration (reuses existing service)
  • Tests: park → verify snapshot. Escalate → verify assessment. Claim from queue.

Phase 4: Resolution Outputs (est. 1.5-2 days)

  • ResolutionOutputGenerator service
  • Resolution API endpoints + push logic
  • KB article generation with dead-end branch "rule out" guidance
  • Tests: resolve multi-branch session → verify 3 outputs → edit → push

Phase 5: AI Description Pipeline (est. 0.5 day)

  • Extend upload endpoint with async ai_description generation
  • Add extracted_content + content_summary for text files
  • Tests: upload image → verify ai_description populated

Phase 6: Frontend (est. 3-4 days)

  • Branch Map sidebar (tree vis, status badges, click-to-switch)
  • Fork Card component (in-chat decision point)
  • Branch transition animation
  • Handoff modal (unified park/escalate)
  • Session queue page
  • Resolution output panel (three-tab view + edit + push)
  • Branch revival UI

Removability Checklist

If this feature is pulled:

  1. Drop tables: session_branches, fork_points, session_handoffs, session_resolution_outputs
  2. Remove columns from ai_sessions: is_branching, active_branch_id, handoff_count, total_active_seconds, total_parked_seconds
  3. Remove columns from ai_session_steps: branch_id, is_fork_point, fork_point_id
  4. Remove columns from file_uploads: uploaded_on_branch_id, uploaded_at_step_id (keep ai_description, extracted_content, content_summary — useful independently)
  5. Remove 'fork' from step_type check constraint
  6. Delete service files: branch_manager.py, branch_aware_prompt_builder.py, handoff_manager.py, resolution_output_generator.py
  7. Delete endpoint files: session_branches.py, session_handoffs.py, session_resolutions.py
  8. Delete frontend components/hooks/API clients listed above
  9. Existing escalation flow, upload pipeline, chat service, PSA integration — all untouched