# Multi-Tree Cones: Branching Conversations from a Single Agent

**Status**: Proposed
**Date**: 2025-12-19
**Author**: Claude Opus 4.5
**Affects**: Cone activation, potentially ClaudeCode

## Executive Summary

Currently, each Cone is bound to exactly one Arbor tree. This proposal allows a single Cone to own **multiple trees**, enabling parallel conversation branches while maintaining a single "current head" for continuity.

**Key changes:**
- Cones track multiple `tree_id`s instead of one
- New `branch` command creates a fresh tree (optionally cloning context from a node)
- New `fresh` flag on `chat` combines branching + first message in one call
- Head always points to exactly one position across all owned trees

```
┌─────────────────────────────────────────────────────────────────┐
│                         Cone: "assistant"                        │
│                         model: claude-haiku                      │
│                         current_head: tree-B/node-5              │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   tree-A (original)              tree-B (branched)               │
│   ┌──────────────┐               ┌──────────────┐                │
│   │ root         │               │ root         │ ← cloned from  │
│   └──────┬───────┘               └──────┬───────┘   tree-A/node-2│
│          │                              │                        │
│   ┌──────┴───────┐               ┌──────┴───────┐                │
│   │ node-1 (user)│               │ node-3 (user)│                │
│   └──────┬───────┘               └──────┬───────┘                │
│          │                              │                        │
│   ┌──────┴───────┐               ┌──────┴───────┐                │
│   │ node-2 (asst)│               │ node-4 (asst)│                │
│   └──────────────┘               └──────┬───────┘                │
│                                         │                        │
│                                  ┌──────┴───────┐                │
│                                  │ node-5 (user)│ ← HEAD         │
│                                  └──────────────┘                │
│                                                                  │
│   tree-C (fresh start)                                           │
│   ┌──────────────┐                                               │
│   │ root (empty) │                                               │
│   └──────────────┘                                               │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
```

---

## The Desire

### Current Limitation

Today, a Cone's identity is tightly coupled to its conversation history:

```
Cone "assistant" → Tree-A → linear conversation
```

If you want to start fresh or explore an alternative branch, you create a **new Cone**. This fragments the agent's identity - different cones, different names, different configurations to manage.

### What We Want

A single Cone should be able to:

1. **Branch from any point** - "Let's explore a different approach starting from where we discussed X"
2. **Start fresh** - "Forget everything, let's begin again" (but keep the same agent config)
3. **Switch between branches** - The head can move to any tree the cone owns
4. **Maintain identity** - Same name, same model, same system prompt across all branches

### The `fresh` Flag

The most common use case is: "Start a new conversation with this prompt."

Rather than two separate calls (`branch` then `chat`), we add a `fresh` flag to `chat`:

```
cone chat --name assistant --prompt "Let's try a different approach" --fresh
```

This atomically:
1. Creates a new empty tree
2. Adds it to the cone's tree list
3. Sets head to the new tree's root
4. Executes the chat with the prompt

The result is a clean conversation start without losing access to previous trees.

### The `branch` Command

For more control, explicit branching:

```
# Branch from a specific node (clone context up to that point)
cone branch --name assistant --from-node node-abc-123 --prompt "What if we tried..."

# Branch fresh (no context)
cone branch --name assistant --fresh --prompt "Starting over..."
```

The `--prompt` is required because a branch without an initial message is just an empty tree with no purpose. The prompt gives the branch its reason to exist.

---

## Conversation Flows

### Flow 1: Exploring Alternatives

```
User: "Help me design an API"
  └─ Assistant: "Here's a REST approach..."
      └─ User: "What about GraphQL?"
          └─ Assistant: "GraphQL would look like..."  ← HEAD

# User wants to explore REST further without losing GraphQL thread
$ cone branch --name assistant --from-node <node-after-REST-response> --prompt "Let's flesh out the REST design"

# Now HEAD is on new tree, continuing REST exploration
# GraphQL tree is preserved, can switch back later
```

### Flow 2: Fresh Start

```
# Long conversation has accumulated, want to start clean
$ cone chat --name assistant --prompt "New topic: help me with testing" --fresh

# HEAD now on new tree with just this prompt
# Old conversation preserved in original tree
```

### Flow 3: Quick Iteration

```
# Trying different phrasings of same question
$ cone chat --name assistant --prompt "Explain monads" --fresh
$ cone chat --name assistant --prompt "Explain monads like I'm 5" --fresh
$ cone chat --name assistant --prompt "Explain monads with a cooking analogy" --fresh

# Three separate trees, each with one Q&A
# Can review all three approaches
```

---

## Data Model Changes

### Current

```
Cone {
    id: ConeId,
    name: String,
    model_id: String,
    tree_id: TreeId,        // Single tree
    canonical_head: NodeId,  // Position in that tree
    ...
}
```

### Proposed

```
Cone {
    id: ConeId,
    name: String,
    model_id: String,
    tree_ids: Vec<TreeId>,   // Multiple trees
    head: Position,          // Current position (tree_id + node_id)
    ...
}
```

The `head` already contains `tree_id`, so switching trees is just updating `head` to point to a different tree.

---

## New Operations

### `cone_branch`

Create a new tree, optionally cloning context from an existing node.

**Parameters:**
- `identifier`: Cone to branch
- `prompt`: Initial message (required)
- `from_node`: Optional - clone context up to this node
- `fresh`: If true, ignore `from_node` and create empty tree

**Returns:** Stream with branch creation events, then chat events

### `cone_chat` with `fresh` flag

**Additional parameter:**
- `fresh`: Optional bool - if true, create new tree before chatting

**Behavior when `fresh=true`:**
1. Create new empty tree
2. Add to cone's tree list
3. Update head to new tree root
4. Proceed with normal chat flow

### `cone_switch`

Move head to a different tree/node.

**Parameters:**
- `identifier`: Cone
- `position`: New head position (tree_id + node_id)

**Returns:** Confirmation with new head position

### `cone_list_trees`

List all trees owned by a cone.

**Parameters:**
- `identifier`: Cone

**Returns:** List of tree summaries (id, created_at, root node, leaf count)

---

## Questions to Resolve

1. **Tree cleanup**: When should old trees be garbage collected? Never automatically? After N days of inactivity? Manual delete only?

2. **Context limits**: When branching with `from_node`, should we limit how much context is cloned? Or trust the user?

3. **Default tree**: Should cones always have a "primary" tree, or are all trees equal?

4. **Branch naming**: Should trees have human-readable names? "main", "graphql-exploration", "attempt-3"?

5. **Cross-cone sharing**: Could one tree be shared between multiple cones? (Probably not - breaks ownership model)

---

## Non-Goals

- **Merging branches**: Git-style merge of conversation branches is out of scope
- **Diff between branches**: Comparing what diverged is interesting but separate
- **Concurrent heads**: One cone = one head, always

---

## Related Documents

- `16680556555233258495_arbor-handles.md` - How trees store messages via handles
- `16680562178783729663_session-improvements.md` - Ephemeral messages (related but different)
