# Conversation title is auto-generated or set on creation

- **Pattern:** `ab-000351` (`ai-ux-patterns.advanced-patterns.conversation-title`)
- **Severity:** info
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000351
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000351)

## Why it matters

A history sidebar full of rows labeled "New conversation" or UUIDs is functionally useless — users cannot find anything, so they start a new chat every time instead of continuing old ones, which defeats the point of persistence entirely. Auto-generated titles turn the archive from a dead dump into a searchable, scannable record of prior work, and they cost roughly a fraction of a cent per conversation when generated by a cheap model like `gpt-4o-mini` or `claude-haiku`.

## Severity rationale

Info because it is polish, not a functional blocker — users can still operate without titles.

## Remediation

After the first assistant response lands, fire a background call to a cheap model asking for a six-word title summarizing the opening exchange. Persist via `UPDATE conversations SET title = $1 WHERE id = $2`. Fall back to the first fifty characters of the user's opening message if the generation fails. Implement in `src/lib/ai/generate-title.ts`.

```tsx
const { text } = await generateText({ model: openai('gpt-4o-mini'), prompt: `Six-word title for: "${firstMessage.slice(0, 200)}"` })
await updateConversationTitle(id, text.trim())
```

## Detection

- **ID:** `conversation-title`
- **Severity:** `info`
- **What to look for:** Count all conversations visible in the history list or sidebar. For each, enumerate the title source: auto-generated by AI, truncated from first message, user-provided, or generic default ("Untitled", "New conversation", UUID). Check for title generation logic: API calls that summarize the first exchange, first-message truncation functions, or user-rename handlers. At least 1 title generation mechanism must produce a maximum 50-character descriptive title.
- **Pass criteria:** Conversations in the history list have human-readable titles — either auto-generated, derived from the first message, or user-provided. Titles must be descriptive and under 60 characters. Report on pass: "Title generation uses X method."
- **Cross-reference:** For conversation persistence patterns including database schema for conversation metadata, see the Data Persistence Fundamentals in the Accessibility Fundamentals Audit.
- **Fail criteria:** All conversations in history are titled "Untitled", "New conversation", or display a raw UUID/ID — making it impossible to identify conversations at a glance.
- **Skip (N/A) when:** Same as `conversation-organization` — single-session application with no persisted history.
- **Detail on fail:** `"Conversation history present but titles default to 'New conversation' or UUID — no auto-title generation or first-message truncation logic found"`.
- **Remediation:** Auto-title is a small but high-impact quality-of-life feature. A simple approach:

  ```tsx
  // After first AI response, generate a title
  const generateTitle = async (firstMessage: string) => {
    const { text } = await generateText({
      model: openai('gpt-4o-mini'), // Use cheap model for titles
      prompt: `Generate a short, descriptive title (max 6 words) for a conversation that starts with: "${firstMessage.slice(0, 200)}"`,
    })
    await updateConversationTitle(conversationId, text.trim())
  }
  ```

  Or simply use the first 50 characters of the first message:

  ```tsx
  const title = firstUserMessage.slice(0, 50) + (firstUserMessage.length > 50 ? '...' : '')
  ```

---

Taxons: user-experience

HTML version: https://auditbuffet.com/patterns/ab-000351
