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