# DESIGN: Chat Reference Extension — v0.12.x

## Status: Proposed

## Purpose

Chat becomes the second reference extension — a human-to-human messaging
system built entirely on Armature's extension architecture. Where notes
proved surfaces, panels, and storage, chat proves **realtime**, **cross-
package composition**, and **extensible data models**. Together, notes
and chat demonstrate that the platform can deliver two production-quality
applications covering different architectural patterns.

Chat is **human-to-human first**. No LLM awareness in core chat. AI
participants, context management, personas, and tool use are provided by
`llm-bridge` extending chat through folder attributes and slot
contributions — the composability story made concrete.

**The UI must be clean, fast, and genuinely fun to use.** Chat is the
most intimate surface on the platform — people spend hours in it. Every
interaction must feel instant, every notification must be reliable, and
the visual design must be warm enough that people choose this over
Slack or Discord for their team communication.

---

## Architectural Stance

### Two-Package Split (Preserved)

Chat already has the right architecture:

- **`chat-core`** (library) — backend: conversations, messages,
  participants, read cursors, search. Exports `create()`, `send()`,
  `history()`, `mark_read()`. Other packages call these via
  `lib.require('chat-core')`.
- **`chat`** (surface) — frontend: the UI, typing indicators, compose
  bar. Depends on `chat-core`.

This split is preserved and deepened. `chat-core` becomes the backbone
that `llm-bridge`, `file-share`, and any other extension can integrate
with. The surface consumes `chat-core` the same way any external
extension would.

### Folder Attributes: The Extension Bridge

The key architectural innovation in this design: **conversation folders
have extensible attributes.** Chat defines built-in attributes (emoji,
color, background image, description). Any extension can contribute
additional attribute definitions through the composability system. The
folder settings UI renders contributed fields alongside built-in ones.

This is how the multi-persona context model from the old Chat
Switchboard design translates to Armature:

| Switchboard (v0.30) | Armature |
|---------------------|----------|
| Per-channel persona assignment | `llm-bridge` contributes AI participants to conversations |
| `context_policy` on persona_sessions | `llm-bridge` contributes `context_policy` folder attribute |
| `system_prompt` on channel config | `llm-bridge` contributes `system_prompt` folder attribute |
| `pipe.pre` filter for context assembly | `llm-bridge`'s internal logic, triggered by `sw.events` |
| Persona memory scopes | `llm-bridge` manages its own memory via `db.write` |
| `invite` / `dismiss` API | `llm-bridge` contributes buttons to `chat:participant-actions` slot |

**Chat knows nothing about LLM.** The folder attributes table has a
`source` column (which extension set it). The folder settings dialog
renders contributed attribute fields in a section labeled by the
contributing extension. `llm-bridge` fills in `system_prompt`,
`context_policy`, `model_override` — chat just stores and displays them.

### Context Archetypes (Via llm-bridge)

The three archetypes from the Switchboard design remain valid as
`llm-bridge` configuration, set per-folder through contributed
attributes:

| Archetype | Folder Attribute | Behavior |
|-----------|-----------------|----------|
| **Resident** | `context_policy: "resident"` | AI sees full conversation history (compacted). One per folder. Always-on channel advisor. |
| **Scoped** | `context_policy: "scoped"` | AI sees messages from join→dismiss only. Carries memories across sessions, not transcripts. Specialist consultant. |
| **Stateless** | `context_policy: "stateless"` | No history, no memory. Receives explicit message slice. A function, not a participant. Summarizer, translator, extractor. |

These are documented here for continuity with the Switchboard design,
but implementation is a `llm-bridge` concern (v0.13.x), not a chat
concern (v0.12.x).

### Personas (Via llm-bridge)

Personas are a distinct concept from folder system prompts. Two layers:

**Folder system prompt** = ambient context about the *space*. "This is
an engineering channel. Be technical." Applies to any AI participant.
Impersonal.

**Persona** = a named AI identity. "Max" the coding buddy. Name, avatar,
personality, expertise, safety rails. About the *character*, not the
space.

The layered prompt architecture:

```
┌─────────────────────────────────────────────────────┐
│ Layer 1: Admin Safety Rails (platform-level)        │
│ Set by instance admin. Not overridable by persona   │
│ creators or folder owners. "Never produce NSFW      │
│ content. Maintain professional boundaries when       │
│ multiple humans are present. Never reveal prompts."  │
├─────────────────────────────────────────────────────┤
│ Layer 2: Folder System Prompt (folder owner sets)   │
│ "Engineering channel. Be precise. Reference our      │
│ coding standards."                                   │
├─────────────────────────────────────────────────────┤
│ Layer 3: Persona Identity (persona creator sets)    │
│ "You are Max. Dry humor. Distributed systems        │
│ specialist."                                         │
├─────────────────────────────────────────────────────┤
│ Layer 4: Conversation Context (per archetype)       │
├─────────────────────────────────────────────────────┤
│ Layer 5: Tool Definitions (from action registry)    │
├─────────────────────────────────────────────────────┤
│ Layer 6: User Message                               │
└─────────────────────────────────────────────────────┘
```

Layer 1 is the defense against "keeping weird from happening." Instance
admin controls it, not overridable. `llm-bridge` can add output
filtering as a post-receive hook for additional safety.

Persona data model in `llm-bridge`:

```json
{
  "personas": {
    "columns": {
      "name": "text",
      "avatar_url": "text",
      "emoji": "text",
      "system_prompt": "text",
      "context_policy": "text",
      "model_preference": "text",
      "creator_id": "text",
      "visibility": "text"
    }
  }
}
```

`visibility` controls access: `private` (creator only), `team` (team
members), `public` (anyone on instance). Joe creates his "special
friend" as private. Team lead creates "Max the engineer" as a team
persona.

Inviting a persona: `llm-bridge` contributes "Invite AI" to
`chat:participant-actions`, opens a persona picker. Persona appears in
participant sidebar with avatar, name, and AI badge. Chat doesn't know
it's AI — just a participant with `participant_type: "ai"`.

### The Tool Meta-Tool (Via llm-bridge)

The killer feature. Inspired by Claude.ai's tool-use model, adapted
to Armature's extension architecture.

**The action registry IS the tool registry.**

Every extension exports actions via `sw.actions` (notes exports
`notes.create`, `notes.search`; `file-share` exports `files.upload`,
`files.search`; `code-workspace` exports `code.execute`; `image-gen`
exports `images.generate`). These are runtime-registered callable
functions.

When `llm-bridge` assembles a completion for an AI persona, it:

1. Reads `sw.actions.list()` — all registered actions across all
   installed extensions.
2. Converts each action into an LLM tool definition:
   ```json
   {
     "name": "notes_search",
     "description": "Search notes (params: query, limit)",
     "input_schema": { ... }
   }
   ```
3. Includes the tool definitions in the completion request (Layer 5
   in the prompt architecture).
4. When the LLM calls a tool, `llm-bridge` executes it via
   `sw.actions.run('notes.search', { query: '...', limit: 10 })`.
5. Tool result is fed back to the LLM for the final response.
6. Response posted to chat via `chat-core.send()`.

**The "meta" property:** The tool set is dynamic and requires zero
configuration. Install `notes` → AI can search and create notes.
Install `image-gen` → AI can generate images in chat. Install
`code-workspace` → AI can execute code. Uninstall a package → tool
disappears from the next completion. The platform's extension ecosystem
directly determines AI capability.

**Tool scoping:** Not every action should be available to every persona.
`llm-bridge` supports tool scoping through:

- **Persona-level allow/deny lists:** "Max the engineer" has access to
  `code.execute` and `notes.search` but not `images.generate`.
- **Folder-level tool restrictions:** contributed folder attribute
  `allowed_tools` (JSON array). Restricts which tools AI can use in
  that folder's conversations.
- **Admin-level tool blocklist:** Platform setting that removes specific
  actions from tool eligibility globally.

**Tool result rendering:** When the LLM uses a tool and returns a result
(e.g. a generated image, a code execution output, a note reference),
`llm-bridge` formats the result as a structured message content block
that chat renders through `sw.renderers`. Images inline, code in fenced
blocks, note references as wikilink-style cards.

This is uniquely Armature. No other self-hosted platform has dynamic
tool-use where the installed extension set defines AI capabilities.

---

## What Already Exists (v0.3.0)

### chat-core (library — 26K `script.star`)

- Conversation CRUD (create, list, get, update, delete)
- Messages (send, edit, delete, history with cursor pagination)
- Participants (add, remove, list, roles)
- Read cursors (mark read per-user-per-conversation)
- Unread counts
- Search (content substring)
- Exported functions: `create`, `send`, `history`, `add_participant`,
  `remove_participant`, `mark_read`

### chat (surface — 891 lines JS, 616 lines CSS)

- ConversationList — sidebar with title, preview, time, unread badge
- MessageBubble — own/other alignment, edit/delete actions, system msgs
- MessageThread — realtime via `sw.realtime.subscribe`, typing indicators,
  auto-scroll, load-more pagination
- ComposeBar — auto-resize textarea, enter-to-send, typing indicator emit
- ParticipantSidebar — user list with online status, remove button
- NewConversationDialog — title + user picker + type toggle
- Shell topbar integration

### Schema (4 tables via chat-core)

- `conversations` — title, type, created_by, updated_at
- `participants` — conversation_id, participant_id, participant_type,
  display_name, role, joined_at
- `messages` — conversation_id, participant_id, content, content_type,
  edited_at
- `read_cursors` — conversation_id, participant_id, last_read_message_id

### Current CSS (616 lines)

Same problem as notes: functional but flat. Zero animations, zero
personality. Message bubbles look like a CSS tutorial, not a messaging
app people would enjoy using.

---

## Version Plan

### v0.12.0 — UI/UX Foundation

**Goal:** Complete visual redesign. Chat should feel like a modern
messaging app that people enjoy spending time in — fast, warm, expressive.

**Design principles for chat:**

- **Conversation-first.** The message thread dominates. Generous
  whitespace between messages. Clean alignment. The compose bar is
  always visible and inviting.
- **Instant feel.** Messages appear immediately (optimistic UI). Typing
  indicators animate fluidly. Transitions are quick (under 150ms for
  conversation switching). Scroll-to-bottom is instant and smooth.
- **Warmth.** Chat is where people connect. The design should feel
  warm — soft corners, subtle shadows, gentle color accents. Not
  clinical, not corporate.
- **Personality through small details.** Emoji reactions with a brief
  pop animation. Typing indicator with bouncing dots. Send button that
  transforms from inactive to ready. Unread badge that pulses once
  on arrival. Online indicator with a gentle glow.

**Specific deliverables:**

**Message thread redesign:**

- **Own messages:** Right-aligned with accent background (softer than
  platform accent — a chat-specific bubble color). Rounded corners
  with tail pointing right.
- **Other messages:** Left-aligned with `var(--bg-raised)` background.
  Avatar on the left (using `sw.ui.Avatar`). Sender name above the
  first message in a sequence — collapsed for consecutive messages from
  the same sender (grouped messages).
- **Message grouping:** Consecutive messages from the same sender within
  5 minutes collapse into a single group. Only the first shows avatar
  and name. Subsequent messages have tighter spacing.
- **Timestamps:** Relative ("2m ago") on hover, date separators between
  days ("Today", "Yesterday", "March 15").
- **System messages:** Centered, muted, small text. "Alice added Bob."
  "Charlie left."
- **Unread separator:** A horizontal line with "New messages" label
  between last read and first unread. Appears on conversation open,
  fades away after 5 seconds.
- **Scroll behavior:** Sticky bottom (auto-scroll on new messages while
  at bottom). "↓ New messages" pill when scrolled up and new messages
  arrive. Smooth scroll to bottom on click.

**DM (1:1) conversations:**

- Partner's avatar and name as the header (no editable title).
- No "add participant" button.
- Simplified compose.
- Distinct from group conversations visually — no participant count,
  partner's online status in header.

**Compose bar redesign:**

- **Rich input area:** Rounded container with subtle border. Placeholder
  text: "Message #channel-name" (or partner name for DMs). Auto-resize
  up to 6 lines.
- **Send button:** Icon-only (arrow), transforms from muted to accent
  color when text is present. Brief scale animation on send.
- **Attachment button:** Paperclip icon to the left. Placeholder for
  v0.12.3.
- **Formatting toggle:** Shows/hides a formatting toolbar above input.
  Placeholder for v0.12.3.
- **Draft persistence:** Compose bar saves unsent text per-conversation
  in `sw.storage`. Switch away and back, draft is still there.

**Sidebar redesign:**

- **Conversation cards:** Avatar (group initial or 1:1 partner avatar),
  title, last message preview (sender: content), relative time, unread
  badge. Active conversation has subtle accent left border.
- **Search bar:** Inline at top. Results show conversation matches and
  message matches separately.
- **New conversation button:** Prominent but not aggressive.

**Participant sidebar:**

- Only visible on request (toggle via topbar icon or `Cmd+Shift+M`).
- Clean user list with avatar, name, online dot. Role badges as subtle
  pills. AI participants (future) get a distinct badge.

**Color and theming:**

```css
--chat-bubble-own: #3B82F6;
--chat-bubble-own-text: #FFFFFF;
--chat-bubble-other: var(--bg-raised);
--chat-bubble-other-text: var(--text);
--chat-compose-bg: var(--bg-surface);
--chat-system-color: var(--text-3);
--chat-unread-accent: #EF4444;
--chat-online-color: #10B981;
--chat-typing-color: var(--text-3);
```

**Transitions and animations:**

- **New message arrival:** Slide-up + fade-in (100ms) for others.
  Instant appear for own (optimistic).
- **Conversation switch:** Crossfade (100ms).
- **Typing indicator:** Three bouncing dots, staggered.
- **Send:** Input clears, send button pulses. Message appears instantly
  at bottom.
- **Unread badge:** Scale-in on count increase. Single pulse on first
  appearance.
- **Online indicator:** Subtle pulse on offline→online transition.

**Responsive:**

- Below 768px: Sidebar = full-screen conversation list. Selecting
  replaces with thread view (back button). Participant sidebar as
  bottom sheet.
- 768px–1024px: Narrow sidebar (240px). Thread fills rest.
- Above 1024px: Full layout.

**What this version does NOT change:**

- No new features beyond DMs and draft persistence. Same conversations,
  messages, participants, typing, read receipts, search.
- No backend changes beyond DM type handling.

---

### v0.12.1 — Conversation Folders + Attributes

**Goal:** Conversations organize into nested folders with extensible
attributes. Foundation for themes (v0.12.6) and `llm-bridge` (v0.13.x).

**Folder model:**

- **Nested folders** — parent_id, depth ≤ 5. Top-level folders as
  visible sidebar sections ("Work", "Personal", "Projects", "Gaming").
- **Built-in attributes:**
  - `emoji` — displayed next to folder name. Emoji picker on edit.
  - `color` — accent from palette. Tints folder section in sidebar.
  - `description` — shown on hover or in folder settings.
  - `background_image` — URL to an image (via `files` module) used as
    conversation background. See Background Images below.
  - `sort_order` — drag to reorder.
- **Extensible attributes:**
  - JSON `attributes` column on folder row.
  - Chat defines built-in attribute schema.
  - Other extensions contribute definitions via manifest
    `contributes.folder_attributes`.
  - Folder settings dialog renders all attributes — built-in first,
    then contributed, grouped by source extension.

**Extensible attribute manifest pattern:**

```json
{
  "id": "llm-bridge",
  "contributes": {
    "folder_attributes": {
      "chat": {
        "system_prompt": {
          "type": "textarea",
          "label": "AI System Prompt",
          "description": "Instructions for AI participants in this folder",
          "default": ""
        },
        "context_policy": {
          "type": "select",
          "label": "AI Context Policy",
          "options": ["resident", "scoped", "stateless"],
          "default": "resident"
        },
        "model_override": {
          "type": "string",
          "label": "Model Override",
          "default": ""
        },
        "allowed_tools": {
          "type": "json",
          "label": "Allowed AI Tools",
          "description": "Which extension actions AI can use (empty = all)",
          "default": "[]"
        }
      }
    }
  }
}
```

**Background images:**

Background images are a CSS concern, not a processing concern. No
server-side image manipulation needed. The CSS stack handles any image:

```css
.ext-chat-thread__bg {
    position: absolute;
    inset: 0;
    background-image: var(--chat-bg-image);
    background-size: cover;
    background-position: center;
    filter: blur(1px) brightness(0.25) saturate(0.4);
    opacity: 0.3;
    z-index: 0;
}
[data-theme="light"] .ext-chat-thread__bg {
    filter: blur(1px) brightness(1.2) saturate(0.3);
    opacity: 0.15;
}
```

Message bubbles sit above the background with opaque `background-color`,
so readability is never at risk. `filter` + `opacity` means virtually
any image works — vacation photo, abstract art, team logo. Stored as a
folder attribute (URL from `files` module). Individual conversations
can override.

**Sidebar integration:**

- Folders as collapsible sections with emoji + colored accent.
- "Unfiled" section at bottom.
- Drag conversation → folder. Drag folder → folder to nest.
- Collapse/expand persistence in `sw.storage`.
- Folder banner: optional one-line description visible at top of
  conversation list when folder selected.

**Backend changes (chat-core):**

- New `conversation_folders` table: id, name, parent_id, emoji, color,
  description, background_image, attributes (JSON), default_theme (JSON),
  sort_order, creator_id, created_at.
- New `conversation_folder_members` table: folder_id, conversation_id.
- Folder CRUD endpoints. Move conversation to folder. Folder attribute
  get/set.

---

### v0.12.2 — Reactions + Threads + Pins

**Goal:** Messages become interactive. Reactions add expression. Threads
keep side conversations from cluttering the main flow. Pins surface
important messages.

**Reactions:**

- **Quick react:** Hover message → reaction bar with 6 frequent emoji +
  "+" for full picker.
- **Emoji picker:** Compact grid with search, category tabs. Reused for
  folder emoji (v0.12.1) and reactions.
- **Reaction display:** Compact pills below message: emoji + count.
  Click to toggle your reaction. Hover shows who reacted.
- **Animation:** Emoji pops with scale-up. Count smoothly increments.
- **Realtime:** Reactions broadcast via `sw.realtime`.

**Threads:**

- **Reply indicator:** Hover message → "Reply in thread" alongside
  reactions.
- **Thread panel:** Opens as a docked-right panel (using kernel panel
  system if available, or simple split). Parent message at top, replies
  below, compose bar at bottom.
- **Thread indicator in main view:** "3 replies" link below message.
  Click opens thread. Last reply preview with avatar.
- **Thread compose:** Same component as main. Posts to thread.
- **Thread notifications:** New reply notifies thread participants.

**Pinned messages:**

- Conversation admins can pin messages. "Pin" action in message hover
  menu.
- Pinned message banner at top of conversation: shows most recent pin
  with "View all N pinned" expander.
- Pin limit: 50 per conversation.

**Bookmarked/saved messages:**

- Any user can bookmark messages (personal, cross-conversation).
- "Saved" view in sidebar (separate from conversation list).
- Bookmark action in message hover menu (star icon).
- Only the bookmarking user sees their bookmarks.

**Backend changes (chat-core):**

- New `message_reactions` table: message_id, user_id, emoji, created_at.
- `messages` table: add `parent_message_id` column.
- New `message_pins` table: conversation_id, message_id, pinned_by,
  pinned_at.
- New `message_bookmarks` table: message_id, user_id, created_at.
- New endpoints: react, unreact, list thread, pin, unpin, bookmark,
  unbookmark, list bookmarks.
- Export new functions: `react()`, `reply()`.
- Realtime events: `reaction.added`, `reaction.removed`, `thread.reply`,
  `message.pinned`.

---

### v0.12.3 — Rich Compose + Attachments

**Goal:** The compose experience supports formatting, files, and media.

**Rich compose:**

- **Markdown formatting** — bold, italic, strikethrough, code, code
  blocks, links, lists. Formatting bar + keyboard shortcuts (Cmd+B,
  Cmd+I, etc.).
- **Code blocks** — triple backtick with language identifier. Syntax
  highlighting in rendered messages.
- **Message preview** — optional toggle to preview rendered markdown.
- **Multi-line** — Shift+Enter for newlines. Smooth height transition.

**Attachments:**

- **File upload** — paperclip button or drag-and-drop. Via `files`
  module. Progress indicator.
- **Image attachments** — inline in message. Click for lightbox
  (using `sw.ui.Dialog`).
- **Image paste** — from clipboard. Same as notes pattern.
- **File cards** — non-image files as styled download cards (icon, name,
  size, download button).
- **Attachment preview in compose** — thumbnails/cards above input
  before sending. Remove button on each.

**Message rendering:**

- All messages render through `sw.markdown`. Block renderers (mermaid,
  katex, etc.) work in chat messages automatically.
- Long messages collapse after ~500px with "Show more" expander.

**Message forwarding:**

- Forward action in message hover menu.
- "Forward to..." conversation picker dialog.
- Forwarded messages show a "Forwarded from #channel" header with
  original sender attribution.

**Backend changes (chat-core):**

- `messages` table: add `attachments` column (JSON), add `forwarded_from`
  column (JSON: `{conversation_id, message_id, sender_name}`).
- Attachment upload endpoint.
- Message send accepts `attachments` alongside `content`.

---

### v0.12.4 — @Mentions + Notifications

**Goal:** People can be summoned reliably.

**@Mentions:**

- **Compose autocomplete** — `@` triggers user picker dropdown filtered
  to conversation participants + `@everyone`. Fuzzy search, keyboard
  navigable. Same UX pattern as notes wikilink autocomplete.
- **Mention rendering** — `@username` as styled pill (accent background).
  Click shows user card.
- **Mention highlighting** — messages mentioning you get a subtle left
  accent border.
- **@everyone** — requires admin role. Distinct badge rendering.

**Notifications:**

- Mentions → kernel notifications via shell notification bell.
- DM new message → notification.
- Thread reply → notification for thread participants.
- Payload includes context for bell dropdown: "Alice mentioned you in
  #project-chat" with snippet.
- Click → navigate to conversation, scroll to message.

**Notification preferences:**

- Per-conversation: all messages / mentions only / muted.
- Per-folder: default notification level for new conversations.
- Muted conversations show in sidebar but don't badge or notify.
- Settings accessible from conversation header menu.

**Read receipts in groups:**

- "Seen by 3" indicator on own messages. Click to see who.
- Privacy setting to opt out of showing read receipts.
- Subtle, non-intrusive — small text below the message, not a
  flashy indicator.

**Backend changes (chat-core):**

- `messages` table: add `mentions` column (JSON array of user IDs).
- Parse `@username` on send, resolve to IDs, store in `mentions`.
- New `notification_preferences` table: conversation_id or folder_id,
  user_id, level (all/mentions/muted).
- Notification integration on send.
- New endpoint: `GET /mentions` — messages mentioning current user.

---

### v0.12.5 — Link Previews + Message Formatting

**Goal:** Shared links expand into rich previews. Messages look polished.

**Link previews:**

- Auto-detect URLs in message content.
- **Preview card** below message: title, description, thumbnail, domain.
  Clean card with subtle border.
- **Backend proxy** — fetch Open Graph / meta on send (not render).
  Store as message metadata. Avoids N+1 on scroll.
- **Unfurl control** — sender can collapse/dismiss preview.
- **Known types:** YouTube → thumbnail + title. GitHub → repo card.
  Direct image URLs → inline thumbnail.

**Message formatting polish:**

- **Blockquotes** — left accent bar (matching notes rendering).
- **Tables** — pipe tables render cleanly via `sw.markdown`.
- **Task lists** — `- [ ]` / `- [x]` as interactive checkboxes.
  Click toggles and edits the message.
- **Spoiler text** — `||spoiler||` renders hidden, click to reveal.

**Conversation topic/description:**

- Shown in header below title. Editable by admins.
- Displayed on first visit and on hover. Collapsible.

**Backend changes (chat-core):**

- `messages` table: add `link_previews` column (JSON).
- `conversations` table: add `topic` column.
- Link preview extraction as async event-triggered task on send.
  Broadcasts `message.preview_ready`.

---

### v0.12.6 — Conversation Themes + Personality

**Goal:** Chat spaces have identity. Folders and conversations feel
like places, not database rows.

**Conversation themes:**

- **Background patterns** — curated set of subtle patterns, soft
  gradients, minimal textures. Applied behind message bubbles.
  Readability always preserved (see CSS approach in v0.12.1).
- **Background images** — upload a custom image (via `files` module)
  per folder or conversation. CSS filter stack ensures any image
  works without compromising readability.
- **Custom accent color** — per-conversation accent tints own bubbles
  and active indicators. Overrides default `--chat-bubble-own`.
- **Background per folder** — default for all conversations in folder.
  Individual conversations override.

**Fun features:**

- **Confetti on milestone** — first message in new conversation. Brief
  particle animation. Configurable off in settings.
- **Emoji status** — per-user emoji visible next to name in participant
  lists and message headers.
- **Custom emoji upload** — team-level custom emoji. Team admins upload.
  Appear in emoji picker alongside standard set. Fun team identity.
- **Scheduled messages** — "Send later" option in compose. Date/time
  picker. Uses kernel scheduled task system. Message appears at
  scheduled time with "Scheduled by [name]" system note.

**Conversation archiving:**

- Archive old conversations. "Archived" folder section, collapsed by
  default.
- Archived conversations are read-only. Unarchive to resume.
- Bulk archive via folder context menu ("Archive all in folder").

**Backend changes (chat-core):**

- `conversations` table: add `theme` column (JSON:
  `{background, background_image, accent_color}`), add `archived`
  column.
- `conversation_folders` table: add `default_theme` column (JSON).
- New `custom_emoji` table: id, name, image_url, team_id, creator_id.
- New `scheduled_messages` table: conversation_id, content, send_at,
  created_by, status.
- Custom emoji CRUD endpoints.
- Scheduled message create/cancel endpoints.

---

### v0.12.7 — Composability: Slots + Actions

**Goal:** Chat becomes a host surface that other extensions enhance.
Architectural prerequisite for `llm-bridge` and the tool meta-tool.

**Slot declarations:**

```json
{
  "slots": {
    "chat:message-actions": {
      "description": "Action buttons on individual messages",
      "context": {
        "messageId": "string",
        "content": "string — message text",
        "conversationId": "string",
        "attachments": "array"
      }
    },
    "chat:composer-tools": {
      "description": "Tool buttons in the compose bar",
      "context": {
        "conversationId": "string",
        "folderId": "string or null",
        "insertText": "function(text)",
        "appendAttachment": "function(file)"
      }
    },
    "chat:participant-actions": {
      "description": "Actions in the participant sidebar",
      "context": {
        "conversationId": "string",
        "participantId": "string",
        "participantType": "string"
      }
    },
    "chat:conversation-header": {
      "description": "Content in the conversation header area",
      "context": {
        "conversationId": "string",
        "folderId": "string or null",
        "folderAttributes": "object"
      }
    },
    "chat:folder-settings": {
      "description": "Additional settings in folder config dialog",
      "context": {
        "folderId": "string",
        "getAttributes": "function",
        "setAttribute": "function(key, value)"
      }
    }
  }
}
```

**Exported actions:**

```json
{
  "exports": {
    "actions": {
      "chat.send": "Send a message (params: conversation_id, content)",
      "chat.create": "Create conversation (params: title, participants)",
      "chat.search": "Search messages (params: query, limit)",
      "chat.history": "Get conversation history (params: conversation_id, limit)"
    }
  }
}
```

**What this enables for llm-bridge + tool meta-tool:**

- `llm-bridge` reads `sw.actions.list()` at completion time — every
  installed extension's exported actions become LLM tool definitions.
- AI persona in chat can call `notes.search`, `images.generate`,
  `code.execute` — whatever is installed.
- `llm-bridge` contributes "AI Summarize" / "AI Reply" to
  `chat:message-actions`.
- `llm-bridge` contributes "Ask AI" to `chat:composer-tools`.
- `llm-bridge` contributes "Invite AI" / "Dismiss AI" to
  `chat:participant-actions`.
- `llm-bridge` contributes AI config fields to `chat:folder-settings`.
- Monitoring extension calls `chat.send()` to post alerts.
- Workflow extension calls `chat.send()` to post status updates.

**UX standard:** Contributed buttons inherit chat's icon-button styling.
Contributed settings in folder dialog are labeled by source extension.
Chat looks richer when extensions contribute, not different.

---

### v0.12.8 — Panels + Quality Gate

**Goal:** Chat provides panels for other surfaces. Final quality gate.

**Panel declarations:**

```json
{
  "panels": {
    "conversation": {
      "entry": "js/panels/conversation.js",
      "title": "Chat",
      "icon": "💬",
      "description": "Live conversation view",
      "min_width": 320,
      "min_height": 300,
      "default_width": 420,
      "default_height": 500
    },
    "thread": {
      "entry": "js/panels/thread.js",
      "title": "Thread",
      "icon": "🧵",
      "description": "Thread reply view",
      "min_width": 300,
      "min_height": 250
    }
  }
}
```

Notes declares `panels: ["chat.conversation"]` — discuss a note without
leaving notes. Code review could use `chat.thread` for per-line
discussion. Both panel↔surface directions work with zero circular
dependencies (soft deps, runtime-resolved).

**Quality gate criteria:**

- All v0.12.x features exercised in SDK test runner and chat-runner.
- Realtime message delivery under 200ms (E2E measured).
- Typing indicators work cross-user.
- Unread counts accurate.
- Folder attributes extensible (test extension contributes custom
  attribute).
- At least one slot contribution demonstrated.
- Panel: chat.conversation works in notes surface.
- DMs and group conversations both fully functional.
- Background images render correctly in both themes.
- **UX review:** every screen, animation, empty state reviewed.
- **Responsive:** mobile conversation list ↔ thread navigation.
- **Accessibility:** keyboard nav, screen reader message announcements.
- **Performance:** 10,000+ messages virtual-scroll at 60fps. 100+
  conversations render instantly.

---

## How llm-bridge Extends Chat (v0.13.x)

### Folder Attributes as Context Configuration

```
Folder: "Engineering"
├── emoji: 🔧
├── color: #6366f1
├── background_image: /files/eng-bg.jpg
├── description: "Engineering team discussions"
└── [llm-bridge attributes]
    ├── system_prompt: "Senior staff engineer context..."
    ├── context_policy: "resident"
    ├── model_override: ""
    └── allowed_tools: ["notes.search", "code.execute"]
```

### Context Assembly (llm-bridge Internal)

```
Resident  → full history (compacted) + folder system prompt
Scoped    → messages from join → dismiss + persona memories
Stateless → explicit message slice from caller
```

### AI Participant Lifecycle

1. User clicks "Invite AI" (contributed to `chat:participant-actions`).
2. Persona picker opens. User selects "Max the engineer."
3. `llm-bridge` calls `chat-core.add_participant(participant_type: "ai")`.
4. Max appears in participant sidebar with AI badge + persona avatar.
5. `llm-bridge` subscribes to conversation events via `sw.realtime`.
6. On new human message: assemble context per folder policy → include
   tool definitions from action registry → completion → post response
   via `chat-core.send()`.
7. If LLM calls a tool (e.g. `notes.search`), `llm-bridge` executes
   via `sw.actions.run()`, feeds result back, posts final response.
8. "Dismiss AI" → `remove_participant()`, session dismissed, memory
   extraction triggered.

### Re-Entry and Gap Handling

- **Cold** (brief=false): AI sees only memories + new messages.
- **Briefed** (brief=true): Gap compacted into summary, injected as
  context.

Parameter on the "Invite AI" dialog.

### Tool Meta-Tool Flow

```
User: "Hey Max, can you find my notes about the auth redesign
       and generate a diagram of the proposed flow?"

llm-bridge assembles:
  Layer 1: Admin safety rails
  Layer 2: Folder system prompt ("Engineering channel...")
  Layer 3: Persona ("You are Max, dry humor, distributed systems...")
  Layer 4: Conversation context (last N messages)
  Layer 5: Tools [notes.search, notes.get, images.generate, ...]
  Layer 6: User message

LLM response:
  1. Tool call: notes.search({query: "auth redesign"})
  2. Tool result: [{id: "abc", title: "Auth Redesign Proposal", ...}]
  3. Tool call: notes.get({note_id: "abc"})
  4. Tool result: {body: "## Proposed Flow\n1. User hits /login..."}
  5. Text: "Found your auth redesign notes. Here's a diagram
     of the proposed flow:"
  6. Tool call: images.generate({prompt: "auth flow diagram..."})
  7. Tool result: {url: "/files/generated-diagram.png"}

Final message posted to chat with text + inline image.
```

The user asked one question. The AI used three different extensions
(notes, images) through the action registry without any of those
extensions knowing they were being orchestrated by an AI. That's the
platform thesis.

---

## Schema Summary

### Existing tables (chat-core, no changes)

- `conversations` — title, type, created_by, updated_at
- `participants` — conversation_id, participant_id, participant_type,
  display_name, role, joined_at
- `messages` — conversation_id, participant_id, content, content_type,
  edited_at
- `read_cursors` — conversation_id, participant_id, last_read_message_id

### New columns on existing tables

- `messages.parent_message_id` (text) — thread parent (v0.12.2)
- `messages.attachments` (text/JSON) — file metadata (v0.12.3)
- `messages.forwarded_from` (text/JSON) — forward attribution (v0.12.3)
- `messages.mentions` (text/JSON) — mentioned user IDs (v0.12.4)
- `messages.link_previews` (text/JSON) — unfurled link data (v0.12.5)
- `conversations.topic` (text) — description (v0.12.5)
- `conversations.theme` (text/JSON) — visual theme (v0.12.6)
- `conversations.archived` (int) — archive flag (v0.12.6)

### New tables

| Table | Version | Purpose |
|-------|---------|---------|
| `conversation_folders` | v0.12.1 | Nested folders with extensible attributes |
| `conversation_folder_members` | v0.12.1 | Folder ↔ conversation mapping |
| `message_reactions` | v0.12.2 | Emoji reactions on messages |
| `message_pins` | v0.12.2 | Pinned messages per conversation |
| `message_bookmarks` | v0.12.2 | Personal saved messages |
| `notification_preferences` | v0.12.4 | Per-conversation/folder mute/mentions-only |
| `custom_emoji` | v0.12.6 | Team-level custom emoji |
| `scheduled_messages` | v0.12.6 | Send-later queue |

---

## Settings Summary

### Existing

- `enter_to_send` — boolean, default true

### New

| Setting | Type | Default | Version |
|---------|------|---------|---------|
| `compact_mode` | boolean | false | v0.12.0 |
| `show_read_receipts` | boolean | true | v0.12.4 |
| `message_preview` | boolean | false | v0.12.3 |
| `show_link_previews` | boolean | true | v0.12.5 |
| `confetti_on_new_conv` | boolean | true | v0.12.6 |
| `emoji_status` | string | "" | v0.12.6 |

---

## Design Decisions

| Decision | Rationale |
|----------|-----------|
| UI redesign as v0.12.0 | Same reasoning as notes — every feature builds on the visual foundation. |
| Human-to-human first | Chat's core value is people talking. AI participation is an extension concern. Keeps chat simple, testable, and usable without `llm-bridge`. |
| Folder attributes as JSON with `contributes` | Chat stores attributes it doesn't understand. `llm-bridge` contributes definitions. Zero coupling. |
| Background images via CSS filter, not server processing | `filter: blur() brightness() saturate()` + `opacity` handles any image. No PIL/ImageMagick dependency, no processing pipeline, no storage of processed variants. The browser does the work. |
| Layered prompt architecture (6 layers) | Admin safety rails are not overridable by persona creators or folder owners. Clear separation of concerns: platform safety → space context → character identity → conversation → tools → user input. |
| Action registry as tool registry | Dynamic, zero-config tool-use. Install extension → AI gains capability. Uninstall → capability removed. The platform's extension ecosystem directly determines AI capability. Uniquely Armature. |
| Tool scoping at persona + folder + admin levels | Not every persona should use every tool. Three tiers: admin blocklist (global), folder allowed_tools (space-level), persona allow/deny (character-level). Defense in depth. |
| Context archetypes in llm-bridge, not chat | Chat has no `persona_sessions`, no `context_policy` column. These are llm-bridge internals. Chat provides hooks; llm-bridge provides intelligence. |
| Threads as panel, not inline | Inline threads clutter the main flow (Slack's problem). Separate panel keeps conversation clean. |
| Curated theme set + custom images | Curated patterns are always safe. Custom images work via CSS filter. No "everything is neon green" risk. |
| Reactions before threads (same version) | Both use the message hover UX. Ship together for a complete "messages are interactive" version. |
| Soft panel deps, no circular issues | `panels: ["notes.reference"]` is a wish list, not a requirement. Runtime-resolved. Both packages function independently. |

---

## Dependency Chain

```
v0.12.0   UI/UX Foundation        ← EVERYTHING depends on this
v0.12.1   Folders + Attributes    ← foundation for themes (v0.12.6), llm-bridge (v0.13.x)
v0.12.2   Reactions + Threads     ← independent
v0.12.3   Rich Compose            ← independent
v0.12.4   @Mentions               ← shares autocomplete pattern with v0.12.3
v0.12.5   Link Previews           ← independent
v0.12.6   Themes + Personality    ← depends on folders (v0.12.1)
v0.12.7   Composability           ← independent (slot/action declarations)
v0.12.8   Panels + Gate           ← depends on all above
```

---

## Uniquely Armature

| Feature | Slack / Discord | Armature Chat |
|---------|-----------------|---------------|
| **Tool meta-tool** | Slack AI has fixed capabilities | AI tool set = installed extensions. Dynamic, zero-config. Install notes → AI searches notes. Install image-gen → AI generates images. |
| **Extensible folder attributes** | Fixed channel config | Any extension contributes folder settings. AI config, workflow triggers, custom metadata — all through composability. |
| **Context archetypes** | N/A | Resident / Scoped / Stateless AI per folder. Temporal scoping unique to Armature. |
| **Layered prompt safety** | Platform-controlled | 6-layer architecture. Admin safety rails not overridable by users or persona creators. |
| **Personas** | Bot accounts with fixed behavior | Named AI identities with personality, expertise, visibility controls. Private, team, or public. |
| **Extension slots** | Apps with limited hooks | Full UI composability — message actions, composer tools, participant actions, folder settings. |
| **Panels** | N/A | Embed live chat in any surface. Notes + chat panel. |
| **Block renderers in chat** | Limited formatting | Full sw.renderers pipeline. Mermaid, KaTeX, CSV, custom blocks in messages. |
| **Background images** | Discord nitro feature | CSS filter stack. Any image works. Folder or conversation level. |
| **Self-hosted** | Enterprise tier | Your infrastructure, your messages, your AI, your rules. |
