# Search & Recall + Evidence-Rich Sessions — Design Document

> **Date:** 2026-03-20
> **Status:** Approved
> **Source:** Stack priorities plan items #1 (Search and recall improvements) and #2 (Evidence-rich sessions)

---

## Overview

Two complementary features that make ResolutionFlow a team memory system, not just a flow runner. Engineers can capture proof (screenshots, logs, command output) during troubleshooting via clipboard paste, and search across all past sessions by content, domain, ticket, or semantic similarity.

---

## Feature 1: Evidence-Rich Sessions

### Storage

- **Railway Object Storage** — S3-compatible bucket provisioned via Railway dashboard/CLI
- Backend uses `boto3` with S3-compatible endpoint configured via env vars:
  - `STORAGE_ENDPOINT` — Railway bucket endpoint
  - `STORAGE_ACCESS_KEY` — bucket access key
  - `STORAGE_SECRET_KEY` — bucket secret key
  - `STORAGE_BUCKET_NAME` — bucket name
  - `STORAGE_REGION` — region (default: us-east-1)

### Data Model

New `file_uploads` table:

| Column | Type | Notes |
|--------|------|-------|
| id | UUID PK | |
| account_id | UUID FK → accounts | Tenant scope |
| uploaded_by | UUID FK → users | Who uploaded |
| session_id | UUID FK → ai_sessions (nullable) | Linked session |
| filename | String(255) | Original filename |
| content_type | String(100) | MIME type |
| size_bytes | Integer | File size |
| storage_key | String(500) | S3 object key |
| created_at | DateTime(tz) | |

### API

```
POST   /uploads                    — Multipart file upload, returns upload record with presigned URL
GET    /uploads/{id}/url           — Presigned download URL (time-limited, 1 hour)
GET    /uploads?session_id={id}    — List uploads for a session
DELETE /uploads/{id}               — Delete upload + S3 object
```

### Limits

- Image types: PNG, JPEG, GIF, WebP — max 5MB each
- Text types: .txt, .log, .csv — max 1MB each
- Per-session: 20 files, 50MB total
- Rate limit: 10 uploads/minute per user

### Clipboard Paste UX

A `RichTextInput` component that wraps textareas with clipboard paste support:

- Listens for `paste` event on the textarea
- Detects image blobs in `clipboardData.items`
- On image paste: uploads in background via `POST /uploads`, shows inline thumbnail with progress
- Thumbnail states: uploading (spinner overlay) → success (image preview) → error (retry button)
- Text content and image references stored together in JSONB
- Images rendered as small thumbnails below the textarea, removable with X button

**Where it's used:**
- FlowPilot intake textarea
- Free-text response input (escape hatch)
- Escalation reason textarea
- Session scratchpad

### Evidence in Exports

Extend the existing export service:
- Markdown: images as `![filename](presigned_url)` links
- HTML/PDF: images embedded as `<img>` with presigned URLs
- PSA: image URLs listed as references (ConnectWise notes don't support inline images)

---

## Feature 2: Search & Recall

### Layer 1: Structured Filters

Extend `GET /ai-sessions` with query parameters:

| Param | Type | Filter |
|-------|------|--------|
| problem_domain | string | Exact match on domain |
| matched_flow_id | UUID | Sessions that matched a specific flow |
| confidence_tier | string | guided / exploring / discovery |
| ticket_id | string | PSA ticket ID |
| date_from | datetime | Sessions created after |
| date_to | datetime | Sessions created before |
| q | string | Full-text search (Layer 2) |

Frontend: add filter bar to AI Sessions tab on SessionHistoryPage — domain dropdown, confidence pills, date range, search input. Match the existing Flow Sessions filter pattern.

### Layer 2: Content Search (PostgreSQL FTS)

Add generated `tsvector` column to `ai_sessions`:

```sql
ALTER TABLE ai_sessions ADD COLUMN search_vector tsvector
  GENERATED ALWAYS AS (
    to_tsvector('english',
      coalesce(intake_summary, '') || ' ' ||
      coalesce(resolution_summary, '') || ' ' ||
      coalesce(escalation_reason, '') || ' ' ||
      coalesce(problem_domain, ''))
  ) STORED;

CREATE INDEX idx_ai_sessions_search ON ai_sessions USING gin(search_vector);
```

The `q` parameter uses `plainto_tsquery('english', q)` against this vector. Same pattern as existing tree FTS search.

Extend Command Palette to search AI sessions alongside flows.

### Layer 3: Similar Session Matching

New endpoint: `GET /ai-sessions/{id}/similar?limit=5`

- Generates embedding for the session's intake_summary using existing Voyage AI integration
- New `ai_session_embeddings` table (same pattern as `tree_embeddings`): `session_id`, `embedding` (pgvector)
- Embeddings generated on session creation (after intake) and updated on resolution
- Cosine similarity query against all session embeddings for the account
- Returns top N matches with similarity score and session summary

**Where similar sessions appear:**
- FlowPilot session sidebar: "Similar Past Sessions" section (3-5 matches)
- Session detail page: "Related Sessions" at bottom
- Both show: session name/summary, resolution status, date, similarity %

---

## Implementation Order

1. **File upload infrastructure** — S3 service, file_uploads model, upload/download endpoints
2. **RichTextInput component** — clipboard paste handler, thumbnail rendering, upload integration
3. **Wire into FlowPilot** — intake, free-text, escalation, scratchpad
4. **Evidence in exports** — extend export service with image references
5. **Structured filters** — extend AI session list endpoint + frontend filter bar
6. **Content search (FTS)** — migration for tsvector + GIN index, wire into list endpoint
7. **Command palette session search** — extend CommandPalette with AI session results
8. **Similar session matching** — embeddings table, generation service, similar endpoint
9. **Similar sessions UI** — sidebar section + session detail section
