diff --git a/docs/comments-implementation-guide.md b/docs/comments-implementation-guide.md deleted file mode 100644 index 07f35547c..000000000 --- a/docs/comments-implementation-guide.md +++ /dev/null @@ -1,506 +0,0 @@ -# Comments & Mentions Implementation Guide - -> Implementation guide for adding Google Docs-style comments and @mentions to shared chats in SurfSense. - ---- - -## Table of Contents - -1. [Overview](#overview) -2. [Architecture Decisions](#architecture-decisions) -3. [Database Design](#database-design) -4. [API Design](#api-design) -5. [Permission Model](#permission-model) -6. [Business Rules](#business-rules) -7. [File Structure](#file-structure) -8. [Implementation Phases](#implementation-phases) -9. [Testing Checklist](#testing-checklist) -10. [Out of Scope](#out-of-scope) - ---- - -## Overview - -### Problem Statement - -When team members view a shared AI chat, they cannot discuss specific answers in place. Users currently have to screenshot or copy-paste the output into external tools (Slack/Teams) to ask questions or validate data. This context switching causes friction and fragments organizational knowledge. - -### User Story - -As a user, I want to be able to place and reply to comments on AI responses to let my team know my thoughts without leaving SurfSense. - -### Solution - -Users can place comments on AI chat responses and reply to existing comments, similar to Google Docs, but limited to a single level of nesting per AI response. - ---- - -## Architecture Decisions - -### Threading Model - -``` -AI Response (message_id: 123) -├── Comment A (parent_id: NULL) ← Top-level comment -│ ├── Reply A1 (parent_id: A) ← Reply -│ ├── Reply A2 (parent_id: A) ← Reply -│ └── Reply A3 (parent_id: A) ← Reply -├── Comment B (parent_id: NULL) ← Top-level comment -│ └── Reply B1 (parent_id: B) ← Reply -└── Comment C (parent_id: NULL) ← Top-level comment (no replies) -``` - -- **Multiple top-level comments** allowed per AI response -- **One level of nesting** (replies to comments, but no replies to replies) -- API enforces nesting limit, not DB constraint - -### Comment Anchoring - -- Comments attach to **AI responses only** (not user messages) -- Comments attach to **entire message** (not text selection) -- Validated by checking `message.role == 'assistant'` - -### @Mentions - -- **Storage format:** `@[uuid]` in raw content -- **Display format:** `@Display Name` in rendered content -- Mentions extracted and stored in separate table for notifications -- Only search space members can be mentioned - -### Read/Unread Mentions - -- Simple boolean on mention record -- Marked read when user clicks the mention notification -- No automatic read-on-view (keep it simple) - -### Cascade Behavior - -- Deleting a comment deletes all its replies (DB CASCADE) -- Deleting/regenerating a message deletes its comments (DB CASCADE) - -### Backend-First Architecture - -- All business logic in backend -- Frontend is a thin consumer -- Backend returns computed fields: `can_edit`, `can_delete`, `is_edited`, `content_rendered` - ---- - -## Database Design - -### Table: `chat_comments` - -| Column | Type | Constraints | Description | -|--------|------|-------------|-------------| -| `id` | `SERIAL` | PK | Primary key | -| `message_id` | `INTEGER` | FK → `new_chat_messages(id)` ON DELETE CASCADE, NOT NULL | Which AI response | -| `parent_id` | `INTEGER` | FK → `chat_comments(id)` ON DELETE CASCADE, NULLABLE | NULL = top-level, otherwise = reply | -| `author_id` | `UUID` | FK → `user(id)` ON DELETE SET NULL | Who wrote it | -| `content` | `TEXT` | NOT NULL | Plain text, may contain `@[uuid]` | -| `created_at` | `TIMESTAMPTZ` | NOT NULL, DEFAULT NOW() | Creation time | -| `updated_at` | `TIMESTAMPTZ` | NOT NULL, DEFAULT NOW() | Last edit time | - -**Indexes:** -- `idx_comments_message_id` on `message_id` -- `idx_comments_parent_id` on `parent_id` -- `idx_comments_author_id` on `author_id` -- `idx_comments_created_at` on `created_at` - -### Table: `chat_comment_mentions` - -| Column | Type | Constraints | Description | -|--------|------|-------------|-------------| -| `id` | `SERIAL` | PK | Primary key | -| `comment_id` | `INTEGER` | FK → `chat_comments(id)` ON DELETE CASCADE, NOT NULL | Which comment | -| `mentioned_user_id` | `UUID` | FK → `user(id)` ON DELETE CASCADE, NOT NULL | Who was mentioned | -| `read` | `BOOLEAN` | NOT NULL, DEFAULT FALSE | Has user seen it | -| `created_at` | `TIMESTAMPTZ` | NOT NULL, DEFAULT NOW() | When mentioned | - -**Constraints:** -- `UNIQUE (comment_id, mentioned_user_id)` - Can't mention same person twice - -**Indexes:** -- `idx_mentions_user_unread` on `(mentioned_user_id) WHERE read = FALSE` (partial index) -- `idx_mentions_comment_id` on `comment_id` - -### Schema Diagram - -``` -new_chat_messages (existing) - │ - │ 1:N - ▼ -┌──────────────────────────────────┐ -│ chat_comments │ -├──────────────────────────────────┤ -│ id (PK) │ -│ message_id (FK) │ -│ parent_id (FK, self-ref) │ -│ author_id (FK → user) │ -│ content │ -│ created_at │ -│ updated_at │ -└──────────────────────────────────┘ - │ - │ 1:N - ▼ -┌──────────────────────────────────┐ -│ chat_comment_mentions │ -├──────────────────────────────────┤ -│ id (PK) │ -│ comment_id (FK) │ -│ mentioned_user_id (FK → user) │ -│ read │ -│ created_at │ -└──────────────────────────────────┘ -``` - ---- - -## API Design - -### Endpoints - -| Method | Endpoint | Purpose | -|--------|----------|---------| -| `GET` | `/api/v1/messages/{message_id}/comments` | List comments with replies | -| `POST` | `/api/v1/messages/{message_id}/comments` | Create top-level comment | -| `POST` | `/api/v1/comments/{comment_id}/replies` | Reply to a comment | -| `PUT` | `/api/v1/comments/{comment_id}` | Edit comment (author only) | -| `DELETE` | `/api/v1/comments/{comment_id}` | Delete comment + replies | -| `GET` | `/api/v1/users/me/mentions` | List user's mentions | -| `POST` | `/api/v1/mentions/{mention_id}/read` | Mark mention as read | -| `POST` | `/api/v1/users/me/mentions/read-all` | Mark all mentions read | - -### Request Schemas - -**Create/Update Comment:** -```json -{ - "content": "string (1-5000 chars)" -} -``` - -### Response Schemas - -**Comment Response:** -```json -{ - "id": 1, - "message_id": 123, - "content": "Is this accurate? @[uuid-here]", - "content_rendered": "Is this accurate? @John Doe", - "author": { - "id": "uuid", - "display_name": "Jane Smith", - "avatar_url": "https://...", - "email": "jane@example.com" - }, - "created_at": "2024-01-15T10:42:00Z", - "updated_at": "2024-01-15T10:42:00Z", - "is_edited": false, - "can_edit": true, - "can_delete": true, - "reply_count": 2, - "replies": [ - { - "id": 2, - "content": "Yes, verified", - "content_rendered": "Yes, verified", - "author": { ... }, - "created_at": "...", - "updated_at": "...", - "is_edited": false, - "can_edit": false, - "can_delete": false - } - ] -} -``` - -**Comment List Response:** -```json -{ - "comments": [ ... ], - "total_count": 5 -} -``` - -**Mention Response:** -```json -{ - "id": 1, - "read": false, - "created_at": "2024-01-15T10:42:00Z", - "comment": { - "id": 5, - "content_preview": "Hey, can you check...", - "author": { - "display_name": "John Doe", - "avatar_url": "..." - }, - "created_at": "..." - }, - "context": { - "thread_id": 123, - "thread_title": "Q3 Analysis", - "message_id": 456, - "search_space_id": 1, - "search_space_name": "Finance Team" - } -} -``` - -**Mention List Response:** -```json -{ - "mentions": [ ... ], - "unread_count": 3 -} -``` - ---- - -## Permission Model - -### New Permissions - -Add to `Permission` enum in `db.py`: - -| Permission | Value | -|------------|-------| -| `COMMENTS_CREATE` | `"comments:create"` | -| `COMMENTS_READ` | `"comments:read"` | -| `COMMENTS_DELETE` | `"comments:delete"` | - -### Default Role Assignments - -| Role | `comments:read` | `comments:create` | `comments:delete` | -|------|-----------------|-------------------|-------------------| -| Owner | ✅ | ✅ | ✅ | -| Admin | ✅ | ✅ | ✅ | -| Editor | ✅ | ✅ | ❌ | -| Viewer | ✅ | ✅ | ❌ | - -### Authorization Rules - -| Action | Who Can Do It | -|--------|---------------| -| Read comments | Anyone with `comments:read` | -| Create comment | Anyone with `comments:create` | -| Create reply | Anyone with `comments:create` | -| Edit comment | Author only | -| Delete own comment | Author | -| Delete any comment | Anyone with `comments:delete` | - ---- - -## Business Rules - -### Validation Rules - -1. **Message must be AI response:** `message.role == 'assistant'` -2. **Reply parent must be top-level:** `parent.parent_id IS NULL` -3. **Content not empty:** 1-5000 characters -4. **Mentioned users must be search space members** - -### Computed Fields (Backend Returns) - -| Field | Logic | -|-------|-------| -| `is_edited` | `updated_at > created_at` | -| `can_edit` | `comment.author_id == current_user.id` | -| `can_delete` | `author_id == user.id OR has_permission("comments:delete")` | -| `content_rendered` | Replace `@[uuid]` with `@Display Name` | -| `reply_count` | Count of replies | - -### Mention Processing - -1. On comment create/update, parse `@[uuid]` patterns from content -2. Validate each UUID is a member of the search space -3. Insert/update records in `chat_comment_mentions` -4. Invalid UUIDs: silently ignore (don't create mention record) - -### Error Responses - -| Scenario | HTTP Status | Message | -|----------|-------------|---------| -| Message not found | 404 | "Message not found" | -| Message is not AI response | 400 | "Comments can only be added to AI responses" | -| Comment not found | 404 | "Comment not found" | -| Cannot reply to reply | 400 | "Cannot reply to a reply" | -| Not authorized to edit | 403 | "You can only edit your own comments" | -| Not authorized to delete | 403 | "You do not have permission to delete this comment" | -| Mention not found | 404 | "Mention not found" | - ---- - -## File Structure - -``` -surfsense_backend/app/ -├── routes/ -│ └── comments_routes.py # All comment endpoints -│ -├── services/ -│ └── comments_service.py # Business logic + DB access -│ -├── schemas/ -│ └── comments.py # Request/response schemas -│ -├── utils/ -│ └── comments.py # Mention parsing, helpers -│ -├── db.py # Add models (ChatComment, ChatCommentMention) -│ -└── alembic/versions/ - └── XX_add_comments_tables.py # Migration -``` - -## Implementation Phases - -### Phase 1: Foundation (P0) - -| Task | Description | -|------|-------------| -| 1.1 | Create `chat_comments` table migration | -| 1.2 | Create `chat_comment_mentions` table migration | -| 1.3 | Add `ChatComment` model to `db.py` | -| 1.4 | Add `ChatCommentMention` model to `db.py` | -| 1.5 | Add comment permissions to `Permission` enum | -| 1.6 | Update `DEFAULT_ROLE_PERMISSIONS` | - -### Phase 2: Core CRUD (P0) - -| Task | Description | -|------|-------------| -| 2.1 | Create Pydantic schemas in `schemas/comments.py` | -| 2.2 | Implement `GET /messages/{id}/comments` | -| 2.3 | Implement `POST /messages/{id}/comments` | -| 2.4 | Implement `POST /comments/{id}/replies` | -| 2.5 | Implement `PUT /comments/{id}` | -| 2.6 | Implement `DELETE /comments/{id}` | - -### Phase 3: Mentions (P1) - -| Task | Description | -|------|-------------| -| 3.1 | Create mention parser in `utils/comments.py` | -| 3.2 | Validate mentioned users are search space members | -| 3.3 | Insert mentions on comment create/update | -| 3.4 | Return `content_rendered` with names resolved | -| 3.5 | Implement `GET /users/me/mentions` | -| 3.6 | Implement `POST /mentions/{id}/read` | -| 3.7 | Implement `POST /users/me/mentions/read-all` | - -### Phase 4: Authorization (P1) - -| Task | Description | -|------|-------------| -| 4.1 | Check `comments:read` on list endpoint | -| 4.2 | Check `comments:create` on create/reply | -| 4.3 | Check author-only on edit | -| 4.4 | Check author OR `comments:delete` on delete | -| 4.5 | Validate message is AI response | -| 4.6 | Validate parent is top-level | - -### Phase 5: Response Enrichment (P1) - -| Task | Description | -|------|-------------| -| 5.1 | Return `can_edit` per comment | -| 5.2 | Return `can_delete` per comment | -| 5.3 | Return `is_edited` | -| 5.4 | Return author info (name, avatar, email fallback) | -| 5.5 | Return `reply_count` per top-level comment | - -### Phase 6: Edge Cases (P2) - -| Task | Description | -|------|-------------| -| 6.1 | Handle deleted user (author_id SET NULL) | -| 6.2 | Handle mention of user no longer in search space | -| 6.3 | Clear error when replying to deleted comment | - ---- - -## Testing Checklist (will be done manual) - -### Functional Tests - -- [ ] Create comment on AI response -- [ ] Create comment on user message (should fail) -- [ ] Reply to comment -- [ ] Reply to reply (should fail) -- [ ] Edit own comment -- [ ] Edit other's comment (should fail) -- [ ] Delete own comment (and verify replies deleted) -- [ ] Delete other's comment as owner/admin -- [ ] Delete other's comment as editor/viewer (should fail) - -### Mention Tests - -- [ ] Mention valid user creates mention record -- [ ] Mention invalid UUID is ignored -- [ ] Mention non-member is ignored -- [ ] `content_rendered` shows display names -- [ ] List mentions returns correct data -- [ ] Mark mention read updates `read` flag -- [ ] Mark all read updates all mentions - -### Edge Case Tests - -- [ ] Comment with 10+ replies scrolls properly (frontend) -- [ ] Delete parent comment cascades to replies -- [ ] Regenerate AI response deletes comments -- [ ] Comment on one-word AI response (frontend min height) -- [ ] User A deletes comment while User B replies → clear error - -### Permission Tests - -- [ ] Viewer can read comments -- [ ] Viewer can create comments -- [ ] Viewer cannot delete other's comments -- [ ] Owner can delete any comment - ---- - -## Out of Scope - -The following are explicitly NOT included in v1: - -| Feature | Reason | -|---------|--------| -| Multiple threads per message | Simplicity | -| Text selection comments | Complexity | -| Rich text (bold/italic/images) | Complexity | -| Email/push notifications | Infrastructure | -| Emoji reactions | Scope | -| Comment on user messages | Focus on AI responses | -| Nested replies (> 2 levels) | UX complexity | - ---- - -## Future Considerations (Not Now) - -These are documented for future reference but NOT to be implemented now: - -1. **Stale comment detection:** Store `message_content_hash` to detect if AI response changed -2. **Real-time sync:** Electric-SQL integration for live updates -3. **Notification center:** Dedicated page for all notifications -4. **Email digests:** Periodic email summaries of mentions -5. **Comment reactions:** Thumbs up, etc. - ---- - -## References - -- Existing patterns: `routes/new_chat_routes.py`, `services/connector_service.py` -- Permission system: `db.py` (Permission enum, DEFAULT_ROLE_PERMISSIONS) -- Schema patterns: `schemas/new_chat.py` - -# Important rules - -- Never do actions in bulk , you will always need my approval before doing something i dod not specifically mention in the prompt -- After each item task , we need to commit , for the backend we need to make sure , there are no ruff errors (ruff check, ruff format); for the frontend we need to run pnpm format:fix at web project. -- Commits should have minimal message and use --no-verify flag \ No newline at end of file