---
phase: 24-search-history-branching
plan: "00"
subsystem: db-schema-shared-types
tags: [migrations, drizzle, types, validators, test-stubs]
dependency_graph:
  requires: []
  provides:
    - 0050_add_branch_columns migration
    - 0051_add_message_search_vector migration
    - 0052_create_chat_message_bookmarks migration
    - chatMessageBookmarks Drizzle schema
    - ChatMessageSearchResult shared type
    - ChatBookmark shared type
    - searchMessagesSchema validator
    - branchConversationSchema validator
    - Wave 0 test stubs for searchMessages, toggleBookmark, branchConversation, exportConversation
  affects:
    - packages/db
    - packages/shared
    - server/src/__tests__
tech_stack:
  added: []
  patterns:
    - AnyPgColumn type annotation for self-referential FK (matches issues.ts, goals.ts pattern)
    - it.todo() for Wave 0 test scaffolding (matches Phase 21 convention)
key_files:
  created:
    - packages/db/src/migrations/0050_add_branch_columns.sql
    - packages/db/src/migrations/0051_add_message_search_vector.sql
    - packages/db/src/migrations/0052_create_chat_message_bookmarks.sql
    - packages/db/src/schema/chat_message_bookmarks.ts
  modified:
    - packages/db/src/migrations/meta/_journal.json
    - packages/db/src/schema/chat_conversations.ts
    - packages/db/src/schema/chat_messages.ts
    - packages/db/src/schema/index.ts
    - packages/shared/src/types/chat.ts
    - packages/shared/src/validators/chat.ts
    - packages/shared/src/index.ts
    - server/src/__tests__/chat-service.test.ts
    - server/src/__tests__/chat-routes.test.ts
decisions:
  - Used AnyPgColumn type annotation for parentConversationId self-referential FK to resolve TypeScript circular reference — matches existing pattern in issues.ts, goals.ts, execution_workspaces.ts
  - content_search tsvector column intentionally omitted from Drizzle schema — it is a Postgres generated stored column queried via raw sql`` only
completed_date: "2026-04-01"
duration: ~5min
tasks: 2
files: 9
---

# Phase 24 Plan 00: DB Migrations, Schema, Types, and Test Stubs Summary

**One-liner:** Three SQL migrations (branch columns, tsvector search, bookmarks table), Drizzle schema updates, and shared TypeScript types/validators for chat search, bookmarks, and branching with Wave 0 it.todo test stubs.

## Tasks Completed

| Task | Name | Commit | Key Files |
|------|------|--------|-----------|
| 1 | DB migrations and Drizzle schema updates | 430bbbb8 | 0050-0052 SQL files, chat_conversations.ts, chat_message_bookmarks.ts, schema/index.ts |
| 2 | Shared types, validators, and Wave 0 test stubs | e881270c | types/chat.ts, validators/chat.ts, shared/index.ts, chat-service.test.ts, chat-routes.test.ts |

## What Was Built

### Task 1: DB Migrations and Drizzle Schema

**Migration 0050_add_branch_columns.sql:** Adds `parent_conversation_id` (self-referential UUID FK with ON DELETE SET NULL) and `branch_from_message_id` to `chat_conversations`, plus a GIN-style index for parent lookups.

**Migration 0051_add_message_search_vector.sql:** Adds `content_search` as a Postgres-generated STORED tsvector column (using `to_tsvector('english', content)`) with a GIN index for full-text search.

**Migration 0052_create_chat_message_bookmarks.sql:** Creates the `chat_message_bookmarks` table with `company_id`, `message_id` (ON DELETE CASCADE), `conversation_id` (ON DELETE CASCADE), plus compound indexes for efficient per-company lookups.

**Drizzle schema changes:**
- `chat_conversations.ts`: Added `parentConversationId` and `branchFromMessageId` columns with `AnyPgColumn` type annotation to resolve TypeScript circular reference. Added `parentIdx` to index object.
- `chat_messages.ts`: Added comment noting the generated tsvector column — it is intentionally absent from the Drizzle schema.
- `chat_message_bookmarks.ts`: New schema file with two compound indexes.
- `schema/index.ts`: Added `chatMessageBookmarks` re-export.

### Task 2: Shared Types, Validators, and Test Stubs

**New types in packages/shared/src/types/chat.ts:**
- `ChatMessageSearchResult` — ranked search result with message, conversation, role, and rank fields
- `ChatMessageSearchResponse` — list wrapper
- `ChatBookmark` — bookmark record shape
- `ChatBookmarkWithMessage` — bookmark with embedded `ChatMessage` and conversation title
- `ChatBookmarkListResponse` — list wrapper
- `ChatBookmarkToggleResponse` — `{ bookmarked: boolean }` for toggle endpoint

**Extended existing types:**
- `ChatConversation` — added `parentConversationId: string | null` and `branchFromMessageId: string | null`
- `ChatConversationListItem` — same two fields added

**New validators in packages/shared/src/validators/chat.ts:**
- `searchMessagesSchema` — `q` (min 2, max 200) + optional `limit` (1-50, coerced)
- `branchConversationSchema` — `branchFromMessageId` as UUID string
- Inferred types: `SearchMessages`, `BranchConversation`

**Wave 0 test stubs (chat-service.test.ts):**
- `describe("searchMessages")` — 3 `it.todo` entries
- `describe("toggleBookmark")` — 2 `it.todo` entries
- `describe("branchConversation")` — 2 `it.todo` entries
- `describe("exportConversation")` — 2 `it.todo` entries

**Wave 0 test stubs (chat-routes.test.ts):**
- `describe("GET /companies/:id/messages/search")` — 2 `it.todo` entries
- `describe("POST /conversations/:id/bookmarks")` — 1 `it.todo` entry
- `describe("POST /conversations/:id/branch")` — 1 `it.todo` entry
- `describe("GET /conversations/:id/export")` — 2 `it.todo` entries

## Verification

- `pnpm --filter @paperclipai/db build` — PASSED
- `pnpm --filter @paperclipai/shared build` — PASSED
- All 7 acceptance criteria for Task 1 — PASSED
- All 10 acceptance criteria for Task 2 — PASSED

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 1 - Bug] Self-referential FK causes TypeScript circular reference**
- **Found during:** Task 1 verification (db build)
- **Issue:** TypeScript error TS7022/TS7024: `chatConversations` implicitly has type `any` because `parentConversationId` references the table being defined
- **Fix:** Import `AnyPgColumn` from `drizzle-orm/pg-core` and annotate the reference callback as `(): AnyPgColumn => chatConversations.id` — matches the existing pattern in `issues.ts`, `goals.ts`, `execution_workspaces.ts`, and `heartbeat_runs.ts`
- **Files modified:** `packages/db/src/schema/chat_conversations.ts`
- **Commit:** 430bbbb8

## Known Stubs

None — all Wave 0 test stubs are intentional `it.todo()` scaffolding per plan specification. They are placeholders for Plans 01-03 to implement.

## Self-Check: PASSED
