---
project_name: 'Keep (Memento Phase 1 MVP AI)'
user_name: 'Ramez'
date: '2026-01-10'
sections_completed: ['technology_stack', 'language_rules', 'framework_rules', 'testing_rules', 'quality_rules', 'workflow_rules', 'anti_patterns']
status: 'complete'
rule_count: 50
optimized_for_llm: true
workflow_type: 'generate-project-context'
---

# Project Context for AI Agents

_This file contains critical rules and patterns that AI agents must follow when implementing code in this project. Focus on unobvious details that agents might otherwise miss._

---

## Technology Stack & Versions

### Core Framework

**Frontend:**
- **Next.js:** 16.1.1 (App Router)
- **React:** 19.2.3
- **TypeScript:** 5.x (strict mode enabled)

**Backend:**
- **Next.js API Routes** (REST)
- **Server Actions** ('use server' directive)
- **Prisma:** 5.22.0 (ORM)
- **Database:** SQLite (better-sqlite3)

**Authentication:**
- **NextAuth:** 5.0.0-beta.30
- **Adapter:** @auth/prisma-adapter

**AI/ML:**
- **Vercel AI SDK:** 6.0.23
- **OpenAI Provider:** @ai-sdk/openai ^3.0.7
- **Ollama Provider:** ollama-ai-provider ^1.2.0
- **Language Detection:** tinyld (to be installed for Phase 1)

**UI Components:**
- **Radix UI:** Multiple primitives (@radix-ui/react-*)
- **Tailwind CSS:** 4.x
- **Lucide Icons:** ^0.562.0
- **Sonner:** ^2.0.7 (toast notifications)

**Utilities:**
- **Zod:** ^4.3.5 (schema validation)
- **date-fns:** ^4.1.0 (date formatting)
- **clsx:** ^2.1.1, **tailwind-merge:** ^3.4.0 (CSS utilities)
- **katex:** ^0.16.27 (LaTeX rendering)
- **react-markdown:** ^10.1.0 (markdown rendering)

**Drag & Drop:**
- **@dnd-kit:** ^6.3.1 (modern DnD library)
- **muuri:** ^0.9.5 (masonry grid layout)

**Testing:**
- **Playwright:** ^1.57.0 (E2E tests)

---

## Critical Implementation Rules

### TypeScript Configuration

**STRICT MODE ENABLED:**
```json
{
  "strict": true,
  "target": "ES2017",
  "moduleResolution": "bundler",
  "jsx": "react-jsx",
  "paths": {
    "@/*": ["./*"]
  }
}
```

**CRITICAL RULES:**
- ✅ All files MUST be typed (no `any` without explicit reason)
- ✅ Use `interface` for object shapes, `type` for unions/primitives
- ✅ Import from `@/` alias (not relative paths like `../`)
- ✅ Props MUST be typed with interfaces (PascalCase names)

**Example:**
```typescript
// ✅ GOOD
interface NoteCardProps {
  note: Note
  onEdit?: (note: Note) => void
}

export function NoteCard({ note, onEdit }: NoteCardProps) {
  // ...
}

// ❌ BAD - any without reason
export function NoteCard({ note, onEdit }: any) {
  // ...
}
```

---

### Component Patterns

**Directives Required:**
- ✅ Server Components: No directive (default in Next.js 16 App Router)
- ✅ Client Components: `'use client'` at TOP of file (line 1)
- ✅ Server Actions: `'use server'` at TOP of file (line 1)

**Example:**
```typescript
// keep-notes/components/ai/ai-suggestion.tsx
'use client'

import { useState } from 'react'
import { Button } from '@/components/ui/button'

export function AiSuggestion() {
  // Interactive component logic
}
```

**Component Naming:**
- ✅ **PascalCase** for component names: `NoteCard`, `LabelBadge`, `AiSuggestion`
- ✅ **kebab-case** for file names: `note-card.tsx`, `label-badge.tsx`, `ai-suggestion.tsx`
- ✅ **UI components** in `components/ui/` subdirectory: `button.tsx`, `dialog.tsx`

**Props Pattern:**
```typescript
// ✅ GOOD - Interface export
export interface NoteCardProps {
  note: Note
  onEdit?: (note: Note, readOnly?: boolean) => void
  isDragging?: boolean
}

export function NoteCard({ note, onEdit, isDragging }: NoteCardProps) {
  // ...
}
```

**Imports Order:**
```typescript
// 1. React imports
import { useState, useEffect } from 'react'

// 2. Third-party libraries
import { formatDistanceToNow } from 'date-fns'
import { Bell } from 'lucide-react'

// 3. Local imports (use @/ alias)
import { Card } from '@/components/ui/card'
import { Note } from '@/lib/types'
import { deleteNote } from '@/app/actions/notes'
```

---

### Server Actions Pattern

**CRITICAL: All server actions MUST follow this pattern:**

```typescript
// keep-notes/app/actions/ai-suggestions.ts
'use server'

import { auth } from '@/auth'
import { revalidatePath } from 'next/cache'
import { prisma } from '@/lib/prisma'

export async function generateTitleSuggestions(noteId: string) {
  // 1. Authentication check
  const session = await auth()
  if (!session?.user?.id) {
    throw new Error('Unauthorized')
  }

  try {
    // 2. Business logic
    const note = await prisma.note.findUnique({
      where: { id: noteId }
    })

    if (!note) {
      throw new Error('Note not found')
    }

    // 3. AI processing
    const titles = await generateTitles(note.content)

    // 4. Revalidate cache
    revalidatePath('/')

    return { success: true, titles }
  } catch (error) {
    console.error('Error generating titles:', error)
    throw new Error('Failed to generate title suggestions')
  }
}
```

**CRITICAL RULES:**
- ✅ `'use server'` at line 1 (before imports)
- ✅ **ALWAYS** check `auth()` session first
- ✅ **ALWAYS** `revalidatePath('/')` after mutations
- ✅ Use `try/catch` with `console.error()` logging
- ✅ Throw `Error` objects (not strings)
- ✅ Return `{ success: true, data }` or throw error

---

### API Routes Pattern

**CRITICAL: All API routes MUST follow this pattern:**

```typescript
// keep-notes/app/api/ai/titles/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'

const requestSchema = z.object({
  content: z.string().min(1, "Content required"),
  noteId: z.string().optional()
})

export async function POST(req: NextRequest) {
  try {
    // 1. Parse and validate request
    const body = await req.json()
    const { content, noteId } = requestSchema.parse(body)

    // 2. Business logic
    const titles = await generateTitles(content)

    // 3. Return success response
    return NextResponse.json({
      success: true,
      data: { titles }
    })
  } catch (error) {
    // 4. Error handling
    if (error instanceof z.ZodError) {
      return NextResponse.json(
        { success: false, error: error.issues },
        { status: 400 }
      )
    }

    console.error('Error generating titles:', error)
    return NextResponse.json(
      { success: false, error: 'Failed to generate titles' },
      { status: 500 }
    )
  }
}
```

**CRITICAL RULES:**
- ✅ Use **Zod schemas** for request validation
- ✅ Return `{ success: true, data: any }` for success
- ✅ Return `{ success: false, error: string }` for errors
- ✅ Handle `ZodError` separately (400 status)
- ✅ Log errors with `console.error()`
- ✅ **NEVER** expose stack traces to clients

**Response Format:**
```typescript
// Success
{ success: true, data: { ... } }

// Error
{ success: false, error: "Human-readable error message" }
```

---

### Database Access Pattern

**SINGLE DATA ACCESS LAYER:**
- ✅ **ONLY** use Prisma ORM (no raw SQL, no direct database access)
- ✅ Import from `@/lib/prisma` (singleton instance)
- ✅ Use `findMany`, `findUnique`, `create`, `update`, `delete`

```typescript
// ✅ GOOD
import { prisma } from '@/lib/prisma'

const notes = await prisma.note.findMany({
  where: { userId: session.user.id },
  orderBy: { createdAt: 'desc' }
})
```

**Prisma Schema Conventions:**
- ✅ **PascalCase** for model names: `User`, `Note`, `Label`, `AiFeedback`
- ✅ **camelCase** for fields: `userId`, `isPinned`, `createdAt`
- ✅ Foreign keys: `{model}Id` format: `userId`, `noteId`
- ✅ Booleans: prefix `is` for flags: `isPinned`, `isArchived`
- ✅ Timestamps: suffix `At` for dates: `createdAt`, `updatedAt`
- ✅ All new fields optional (nullable) for backward compatibility

---

### Naming Conventions

**Database:**
- Tables: **PascalCase** (`AiFeedback`, `MemoryEchoInsight`)
- Columns: **camelCase** (`noteId`, `similarityScore`)
- Indexes: Prisma `@@index([...])` annotations

**API Routes:**
- Collections: **plural** (`/api/notes`, `/api/labels`)
- Items: **singular** (`/api/notes/[id]`)
- Namespace: `/api/ai/*` for AI features

**Components:**
- Component names: **PascalCase** (`NoteCard`, `AiSuggestion`)
- File names: **kebab-case** (`note-card.tsx`, `ai-suggestion.tsx`)

**Functions:**
- Functions: **camelCase** (`getNotes`, `createNote`, `togglePin`)
- Verbs first: `get`, `create`, `update`, `delete`, `toggle`
- Handlers: prefix `handle` (`handleDelete`, `handleTogglePin`)

**Variables:**
- Variables: **camelCase** (`userId`, `isPending`, `noteId`)
- Types/interfaces: **PascalCase** (`Note`, `NoteCardProps`)

---

### State Management

**NO GLOBAL STATE LIBRARIES:**
- ❌ No Redux, Zustand, or similar
- ✅ **React useState** for local component state
- ✅ **React Context** for shared state (User session, Theme, Labels)
- ✅ **React Cache** for server-side caching
- ✅ **useOptimistic** for immediate UI feedback
- ✅ **useTransition** for non-blocking updates

**Example:**
```typescript
'use client'

import { useState, useTransition, useOptimistic } from 'react'

export function NoteCard({ note }: NoteCardProps) {
  const [isPending, startTransition] = useTransition()
  const [optimisticNote, addOptimisticNote] = useOptimistic(
    note,
    (state, newProps: Partial<Note>) => ({ ...state, ...newProps })
  )

  const handleTogglePin = async () => {
    startTransition(async () => {
      addOptimisticNote({ isPinned: !note.isPinned })
      await togglePin(note.id, !note.isPinned)
      router.refresh()
    })
  }
}
```

---

### Error Handling

**Global Pattern:**
```typescript
// API Routes
try {
  // ... code
} catch (error) {
  console.error('Feature name error:', error)
  return NextResponse.json(
    { success: false, error: 'Human-readable message' },
    { status: 500 }
  )
}

// Server Actions
try {
  // ... code
} catch (error) {
  console.error('Feature name error:', error)
  throw new Error('Failed to action')
}
```

**CRITICAL RULES:**
- ✅ Use `console.error()` for logging (not `console.log`)
- ✅ Human-readable error messages (no technical jargon)
- ✅ **NEVER** expose stack traces to users
- ✅ **NEVER** expose internal error details

---

### Import Rules

**ALWAYS use @/ alias:**
```typescript
// ✅ GOOD
import { Button } from '@/components/ui/button'
import { Note } from '@/lib/types'
import { deleteNote } from '@/app/actions/notes'

// ❌ BAD - relative paths
import { Button } from '../../../components/ui/button'
import { Note } from '../lib/types'
```

**Import from Radix UI:**
```typescript
// ✅ GOOD - use @/components/ui/* wrapper
import { Dialog } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'

// ❌ BAD - direct Radix imports
import { Dialog } from '@radix-ui/react-dialog'
import { Button } from '@radix-ui/react-slot'
```

---

### AI Service Pattern

**All AI services follow this structure:**

```typescript
// keep-notes/lib/ai/services/title-suggestion.service.ts
import { getAIProvider } from '@/lib/ai/factory'

export class TitleSuggestionService {
  private provider = getAIProvider()

  async generateSuggestions(content: string): Promise<string[]> {
    try {
      const response = await this.provider.generateText({
        prompt: `Generate 3 titles for: ${content}`,
        maxTokens: 100
      })

      return response.titles
    } catch (error) {
      console.error('TitleSuggestionService error:', error)
      throw new Error('Failed to generate suggestions')
    }
  }
}
```

**CRITICAL RULES:**
- ✅ Use `getAIProvider()` factory (not direct OpenAI/Ollama imports)
- ✅ Services are **stateless classes**
- ✅ Constructor injection of dependencies
- ✅ Methods return `Promise<T>` with error handling
- ✅ No direct database access (via Prisma)

---

### Testing Rules

**Playwright E2E Tests:**
```typescript
// tests/e2e/ai-features.spec.ts
import { test, expect } from '@playwright/test'

test('AI title suggestions appear', async ({ page }) => {
  await page.goto('/')
  await page.fill('[data-testid="note-content"]', 'Test content')
  // Wait for AI suggestions
  await expect(page.locator('[data-testid="ai-suggestions"]')).toBeVisible()
})
```

**CRITICAL RULES:**
- ✅ Use `data-testid` attributes for test selectors
- ✅ Test critical user flows (not edge cases)
- ✅ Use `await expect(...).toBeVisible()` for assertions
- ✅ Tests in `tests/e2e/` directory

---

### Brownfield Integration Rules

**ZERO BREAKING CHANGES:**
- ✅ **ALL new features must extend, not replace existing functionality**
- ✅ Existing components, API routes, and database tables MUST continue working
- ✅ New database fields: **optional** (nullable) for backward compatibility
- ✅ New features: **additive** only (don't remove existing features)

**Example:**
```prisma
// ✅ GOOD - optional new field
model Note {
  // ... existing fields
  language      String?   // NEW: optional
  aiConfidence  Int?      // NEW: optional
}

// ❌ BAD - breaking change
model Note {
  language      String    @default("en")  // BREAKS: non-optional default
}
```

---

### Phase 1 Specific Rules

**AI Features to Implement:**
1. **Title Suggestions** - 3 suggestions after 50+ words
2. **Semantic Search** - Hybrid keyword + vector search with RRF
3. **Paragraph Reformulation** - Clarify, Shorten, Improve Style options
4. **Memory Echo** - Daily proactive note connections (background job)
5. **AI Settings** - Granular ON/OFF controls per feature
6. **Language Detection** - TinyLD hybrid (< 50 words: library, ≥ 50 words: AI)

**Performance Targets:**
- ✅ Title suggestions: < 2s after detection
- ✅ Semantic search: < 300ms for 1000 notes
- ✅ Memory Echo: < 100ms UI freeze (background processing)
- ✅ Language detection: ~8ms (TinyLD) or ~200-500ms (AI)

**Language Support:**
- ✅ System prompts: **English** (stability)
- ✅ User data: **Local language** (FR, EN, ES, DE, FA/Persian + 57 others)
- ✅ TinyLD supports 62 languages including Persian (verified)

---

### Security Rules

**API Keys:**
- ✅ **NEVER** expose API keys to client (server-side only)
- ✅ Store in environment variables (`OPENAI_API_KEY`, `OLLAMA_ENDPOINT`)
- ✅ Use SystemConfig table for provider selection

**Authentication:**
- ✅ **ALL** server actions check `auth()` session first
- ✅ **ALL** API routes require valid NextAuth session
- ✅ Public routes: `/api/auth/*`, login/register pages only

**Privacy:**
- ✅ Ollama path = 100% local (no external API calls)
- ✅ OpenAI path = cloud (verify in DevTools Network tab)
- ✅ User data never logged or exposed

---

### File Organization

**AI Services:**
```
lib/ai/services/
├── title-suggestion.service.ts
├── semantic-search.service.ts
├── paragraph-refactor.service.ts
├── memory-echo.service.ts
├── language-detection.service.ts
└── embedding.service.ts
```

**AI Components:**
```
components/ai/
├── ai-suggestion.tsx
├── ai-settings-panel.tsx
├── memory-echo-notification.tsx
├── confidence-badge.tsx
├── feedback-buttons.tsx
└── paragraph-refactor.tsx
```

**API Routes:**
```
app/api/ai/
├── titles/route.ts
├── search/route.ts
├── refactor/route.ts
├── echo/route.ts
├── feedback/route.ts
└── language/route.ts
```

---

### Development Workflow

**Before implementing ANY feature:**
1. ✅ Read `_bmad-output/planning-artifacts/architecture.md`
2. ✅ Check `project-context.md` for specific rules
3. ✅ Follow naming patterns (camelCase, PascalCase, kebab-case)
4. ✅ Use response format `{success, data, error}`
5. ✅ Add `'use server'` or `'use client'` directives
6. ✅ Import from `@/` alias only

**Quality Checklist:**
- [ ] TypeScript strict mode compliance
- [ ] Zod validation for API routes
- [ ] auth() check in server actions
- [ ] revalidatePath('/') after mutations
- [ ] Error handling with console.error()
- [ ] Response format {success, data/error}
- [ ] Import from @/ alias
- [ ] Component directives ('use client'/'use server')
- [ ] Zero breaking changes
- [ ] Performance targets met

---

## Quick Reference Card

**For AI Agents implementing features:**

| Category | Rule | Example |
|----------|------|---------|
| TypeScript | Strict mode, no `any` | `interface Props { note: Note }` |
| Components | 'use client' at top | `export function Comp() { ... }` |
| Server Actions | 'use server' + auth() + revalidate | `const session = await auth()` |
| API Routes | Zod + {success, data/error} | `return NextResponse.json({ success: true, data })` |
| Database | Prisma only, no raw SQL | `await prisma.note.findMany()` |
| Naming | camelCase vars, PascalCase types | `const userId: string` |
| Imports | @/ alias only | `import { Note } from '@/lib/types'` |
| Error Handling | console.error + human message | `throw new Error('Failed to...')` |
| AI Services | getAIProvider() factory | `const provider = getAIProvider()` |
| Performance | Target < 2s for AI features | `await withTimeout(promise, 2000)` |

---

*Generated: 2026-01-10*
*Project: Keep (Memento Phase 1 MVP AI)*
*Architecture: Based on architecture.md (2784 lines)*
*Status: Ready for AI Agent Implementation*

---

## Usage Guidelines

**For AI Agents:**

- ✅ Read this file **before** implementing any code
- ✅ Follow **ALL** rules exactly as documented
- ✅ When in doubt, prefer the more restrictive option
- ✅ Check `_bmad-output/planning-artifacts/architecture.md` for full context
- ✅ Use response format `{success, data, error}` for API routes
- ✅ Add `'use server'` or `'use client'` directives at top of files
- ✅ Import from `@/` alias only (not relative paths)
- ✅ Validate requests with Zod schemas
- ✅ Check `auth()` session in server actions
- ✅ Call `revalidatePath('/')` after mutations
- ✅ Log errors with `console.error()`

**For Humans:**

- Keep this file **lean and focused** on agent needs
- Update when **technology stack changes**
- Review **quarterly** for outdated rules
- Remove rules that become **obvious over time**
- Add new patterns when they emerge in development

**Maintenance:**

1. **Technology Changes:** Update when adding/removing dependencies
2. **Pattern Evolution:** Add new patterns as they emerge
3. **Bug Prevention:** Add rules when agents make common mistakes
4. **Optimization:** Remove redundant or obvious rules periodically
5. **Review Cycle:** Check quarterly for outdated information

---

**Last Updated:** 2026-01-10

**Optimization Status:** ✅ Optimized for LLM context (50 critical rules, 490 lines)

**Readiness:** ✅ Ready for AI Agent Implementation

---

*Workflow completed: 2026-01-10*
*Generator: Winston (Architect Agent) with Generate Project Context workflow*
*Based on: architecture.md (2784 lines) + existing codebase analysis*
