nexus/.planning/phases/24-search-history-branching/24-00-SUMMARY.md
Nexus Dev d78bdad0a9 docs(24-00): complete foundation plan — migrations, schema, types, test stubs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 15:08:51 +00:00

132 lines
6.5 KiB
Markdown

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