From 14f2e472f897d042776bc144b6029880f7ffde97 Mon Sep 17 00:00:00 2001 From: Lukas May Date: Fri, 30 Jan 2026 20:30:20 +0100 Subject: [PATCH] docs(05-01): improve message schema with sender/recipient and optional response --- .../phases/05-task-dispatch/05-01-PLAN.md | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/.planning/phases/05-task-dispatch/05-01-PLAN.md b/.planning/phases/05-task-dispatch/05-01-PLAN.md index 2bf1c08..58ce44f 100644 --- a/.planning/phases/05-task-dispatch/05-01-PLAN.md +++ b/.planning/phases/05-task-dispatch/05-01-PLAN.md @@ -40,15 +40,26 @@ Output: messages table, MessageRepository port/adapter with full CRUD and tests. Add messages table after agents table. Schema: - id: text primary key -- agentId: text NOT NULL foreign key to agents.id with SET NULL on delete (message persists even if agent deleted) -- type: text enum ['question', 'info', 'error'] NOT NULL default 'question' -- content: text NOT NULL (the question or message text) +- senderType: text enum ['agent', 'user'] NOT NULL (who sent the message) +- senderId: text nullable (agent ID if senderType='agent', null for user) +- recipientType: text enum ['agent', 'user'] NOT NULL (who receives the message) +- recipientId: text nullable (agent ID if recipientType='agent', null for user) +- type: text enum ['question', 'info', 'error', 'response'] NOT NULL default 'info' +- content: text NOT NULL (the message text) +- requiresResponse: integer boolean NOT NULL default 0 (1 = expects reply, 0 = notification only) - status: text enum ['pending', 'read', 'responded'] NOT NULL default 'pending' -- response: text nullable (user's response when status is 'responded') +- parentMessageId: text nullable foreign key to messages.id (links response to original question) - createdAt: integer timestamp NOT NULL - updatedAt: integer timestamp NOT NULL -Add relations: message belongs to agent (nullable after delete). +Sender/recipient foreign keys reference agents.id with SET NULL on delete (messages persist). + +Add relations: +- sender agent (optional) +- recipient agent (optional) +- parent message (for threading responses) +- child messages (replies to this message) + Export Message and NewMessage types. Follow existing patterns: use sqliteTable, relations, InferSelectModel/InferInsertModel. @@ -69,8 +80,11 @@ Create MessageRepository following TaskRepository pattern exactly: - MessageRepository interface with: - create(data: CreateMessageData): Promise - findById(id: string): Promise - - findByAgentId(agentId: string): Promise - - findPending(): Promise (status = 'pending') + - findBySender(type: 'agent'|'user', id?: string): Promise + - findByRecipient(type: 'agent'|'user', id?: string): Promise + - findPendingForUser(): Promise (recipientType='user', status='pending') + - findRequiringResponse(): Promise (requiresResponse=true, status='pending') + - findReplies(parentMessageId: string): Promise - update(id: string, data: UpdateMessageData): Promise - delete(id: string): Promise @@ -82,9 +96,13 @@ Create MessageRepository following TaskRepository pattern exactly: 3. Tests (drizzle/message.test.ts): - Use createTestDatabase helper - - Test create, findById, findByAgentId, findPending, update, delete - - Test pending filter returns only pending messages - - Test response field update when marking as responded + - Test create with agent→user message (question) + - Test create with agent→user message (notification, requiresResponse=false) + - Test findBySender and findByRecipient + - Test findPendingForUser returns only user-targeted pending messages + - Test findRequiringResponse returns only messages needing response + - Test message threading (parentMessageId linking) + - Test update status flow: pending → read → responded 4. Re-export from index files (same pattern as AgentRepository)