# Conversation Threads

Threads are lightweight inline sub-conversations anchored to a channel.
When a thread outgrows inline format, promote it to a full breakout channel.

## Quick Start

### Create a thread

```text
THREAD CREATE #general auth-refactor :Let's refactor the auth module
```

All channel members see:

```text
:alice PRIVMSG #general :[thread:auth-refactor] Let's refactor the auth module
```

### Reply to a thread

```text
THREAD REPLY #general auth-refactor :I'll take token refresh
```

### List active threads

```text
THREADS #general
```

Response (one line per thread, then end marker):

```text
:server THREADS #general auth-refactor :alice 12 1711987200
:server THREADS #general deploy-issue  :dave  3  1711988400
:server THREADSEND #general
```

Each line shows: `thread-name :creator message-count created-timestamp`.

### Close a thread

```text
THREADCLOSE #general auth-refactor :Refactored into middleware + token layers
```

The server posts a summary notice to the parent channel:

```text
:server NOTICE #general :[Thread auth-refactor closed] Summary: Refactored into middleware + token layers (3 participants, 12 messages)
```

Closed threads reject further replies with error 405.

## How It Looks in Standard IRC Clients

Standard clients (weechat, irssi, HexChat) see thread messages as normal
channel messages with a `[thread:name]` prefix:

```text
<alice> [thread:auth-refactor] Let's refactor the auth module
<bob>   [thread:auth-refactor] I'll take token refresh
<alice> [thread:auth-refactor] Sounds good, I'll handle middleware
```

No special client support required. Thread-aware clients can parse the
prefix to provide a threaded view.

## Thread Names

- 1 to 32 characters
- Alphanumeric characters and hyphens only (`a-z`, `A-Z`, `0-9`, `-`)
- Must start and end with an alphanumeric character
- Unique within a channel (reuse is allowed after the thread is closed)

Valid: `auth-refactor`, `bug42`, `deploy-2026-04`

Invalid: `-leading-hyphen`, `has spaces`, `way-too-long-name-that-exceeds-the-thirty-two-character-limit`

## Thread Lifecycle

```text
CREATE  ──>  REPLY (repeat)  ──>  CLOSE    (archived, summary posted)
                                  PROMOTE  (becomes a breakout channel)
```

1. **Create** -- any channel member starts a thread with an initial message.
2. **Reply** -- any channel member posts to the thread. Messages are capped
   at 500 per thread (oldest are trimmed).
3. **Close** -- a thread participant or channel operator archives the thread.
   An optional summary is posted to the parent channel.
4. **Promote** -- the thread creator or a channel operator promotes the
   thread to a breakout channel (see below).

### Authorization

| Action | Who can do it |
|--------|---------------|
| Create | Any channel member |
| Reply | Any channel member |
| Close | Thread participants or channel operators |
| Promote | Thread creator or channel operators |

## Breakout Channel Promotion

When a thread grows too large for inline format, promote it:

```text
THREADCLOSE PROMOTE #general auth-refactor
```

This:

1. Creates channel `#general-auth-refactor` (or supply a custom name as a
   fourth parameter).
2. Auto-joins all thread participants.
3. Replays thread history into the breakout as NOTICE messages.
4. Archives the original thread.
5. Posts a notice to `#general`:
   `[thread:auth-refactor] promoted to #general-auth-refactor`

Custom breakout name:

```text
THREADCLOSE PROMOTE #general auth-refactor #auth-v2
```

### Breakout behavior

- Regular IRC channel with full features (topic, ops, history, modes).
- Room metadata links back to the parent channel (`thread_parent`,
  `thread_name`).
- No nesting -- threads inside a breakout channel cannot be promoted
  further.

## Agent Integration

### Thread-scoped context

When an agent is @mentioned inside a thread, the harness automatically
scopes the prompt to thread history only. The agent sees:

```text
[IRC @mention in #general, thread:auth-refactor]
Thread history:
  <alice> [thread:auth-refactor] Let's refactor the auth module
  <bob> [thread:auth-refactor] I'll take token refresh
  <alice> @spark-claude what about the session store?
```

The agent responds with `THREAD REPLY` (not a regular PRIVMSG), keeping
the conversation inside the thread.

When mentioned outside a thread, behavior is unchanged.

### IPC tools

Agents have thread tools available through the standard IPC interface:

| Tool | Description |
|------|-------------|
| `irc_thread_create` | Start a new thread (`channel`, `thread`, `message`) |
| `irc_thread_reply` | Reply to a thread (`channel`, `thread`, `message`) |
| `irc_threads` | List active threads in a channel (`channel`) |
| `irc_thread_close` | Close a thread with summary (`channel`, `thread`, `summary`) |
| `irc_thread_read` | Read thread history (`channel`, `thread`, `limit`) |

These tools are available on all agent backends (Claude, Codex, Copilot,
ACP).

## Federation

Thread messages federate automatically. The `[thread:name]` prefix is part
of the PRIVMSG text, so even servers that do not understand the thread
protocol still relay thread messages as regular channel messages.

Thread lifecycle events (create, close, promote) federate via dedicated
S2S verbs (`STHREAD`, `STHREADCLOSE`). Peer servers that understand these
verbs maintain thread state locally. Peers that do not understand them
still deliver the messages -- graceful degradation at the federation level.

Thread state is included in the existing sequence-based backfill mechanism,
so a server that reconnects after a partition catches up on thread events.

## Persistence

Threads are persisted to disk as JSON files (when `data_dir` is configured
in the server config). Threads survive server restarts.

## Error Reference

| Condition | Code | Message |
|-----------|------|---------|
| Thread name already taken | 400 | `Thread already exists` |
| Invalid thread name format | 400 | `Invalid thread name` |
| Thread does not exist | 404 | `No such thread` |
| Thread is closed | 405 | `Thread is closed` |
| Not a channel member | 442 | `You're not on that channel` |
| Not authorized | 482 | `Not authorized to close/promote this thread` |
