# Arbor Usage Pattern: Direct vs Plexus

## Overview

Arbor is **infrastructure** - a foundational tree storage layer that other activations depend on directly. Unlike most activations which communicate solely through Plexus message routing, Arbor provides shared storage that activations inject at construction time.

## The Rule

| Operation | Method | Why |
|-----------|--------|-----|
| Tree/node CRUD | Direct ArborStorage | Performance, ownership, atomicity |
| Tree rendering | Direct ArborStorage | Local operation, no external data |
| Path traversal | Direct ArborStorage | Local operation, no external data |
| **Handle resolution** | **Via Plexus** | Arbor doesn't know how to interpret foreign handles |

## Correct Pattern

```rust
use crate::activations::arbor::{Arbor, ArborConfig, ArborStorage};
use crate::activations::cone::{Cone, ConeStorageConfig};

// 1. Create Arbor - this is infrastructure, shared directly
let arbor_config = ArborConfig {
    db_path: dir.path().join("arbor.db"),
    auto_cleanup: false,
    ..Default::default()
};
let arbor = Arbor::new(arbor_config).await?;

// 2. Get the storage layer
let arbor_storage = arbor.storage();

// 3. Cone gets ArborStorage directly - NOT via Plexus
let cone_config = ConeStorageConfig {
    db_path: dir.path().join("cones.db"),
};
let cone = Cone::new(cone_config, arbor_storage.clone()).await?;

// 4. Cone stores handles in Arbor DIRECTLY
let user_handle = ConeStorage::message_to_handle(&user_message, "user");
let user_node_id = arbor_storage
    .node_create_external(&tree_id, Some(parent_id), user_handle, None)
    .await?;

// 5. Render tree - using ArborStorage directly
let tree = arbor_storage.tree_get(&tree_id).await?;
let rendered = tree.render();
```

## Anti-Pattern

```rust
// WRONG: Routing Arbor operations through Plexus
let result = plexus.route("arbor.tree_render", json!({"tree_id": tree_id})).await;

// WRONG: Creating nodes via Plexus routing
plexus.route("arbor.node_create_external", json!({
    "tree_id": tree_id,
    "handle": handle,
})).await;
```

## Why Arbor is Special

### 1. Performance
Tree operations are frequent and latency-sensitive. Plexus routing adds unnecessary serialization/deserialization overhead for operations that should be direct method calls.

### 2. Ownership
Activations "own" their Arbor trees:
- Cone creates trees for conversations
- ClaudeCode creates trees for sessions

They need direct control over tree lifecycle and structure.

### 3. Atomicity
Complex tree operations may require multiple steps within a transaction. Direct storage access enables this atomicity.

### 4. Type Safety
Direct ArborStorage usage provides compile-time type safety. Handle types like `ConeHandle` can be used directly without JSON serialization.

## When Plexus IS Needed

The **only** case where Plexus routing relates to Arbor is **cross-plugin handle resolution**:

```rust
// Arbor tree contains external nodes with handles like:
// Handle {
//     plugin_id: CONE_PLUGIN_ID,
//     method: "chat",
//     meta: ["msg-123", "user", "alice"]
// }

// The tree can be rendered directly:
let tree = arbor_storage.tree_get(&tree_id).await?;
let rendered = tree.render();  // Shows handle references

// But to resolve what the handle POINTS TO, you need Plexus
// because Arbor doesn't know how to interpret Cone handles
let resolved = plexus.resolve_handle(handle).await?;
```

This is the distinction:
- **Tree structure** (nodes, edges, parents, children): Direct ArborStorage
- **Handle content** (what the handle references): Via Plexus to the owning plugin

## Dependency Injection Pattern

Activations receive ArborStorage at construction time:

```rust
pub struct Cone<P: HubContext = NoParent> {
    storage: Arc<ConeStorage>,
    // ...
}

impl<P: HubContext> Cone<P> {
    pub async fn with_context_type(
        config: ConeStorageConfig,
        arbor: Arc<ArborStorage>,  // Injected directly
    ) -> Result<Self, String> {
        let storage = ConeStorage::new(config, arbor).await?;
        // ...
    }
}
```

The ConeStorage then holds the ArborStorage reference:

```rust
pub struct ConeStorage {
    pool: SqlitePool,
    arbor: Arc<ArborStorage>,  // Direct reference, not Plexus
}

impl ConeStorage {
    pub fn arbor(&self) -> &ArborStorage {
        &self.arbor
    }
}
```

## See Also

- `src/activations/cone/tests.rs` - Integration tests demonstrating correct pattern
- `src/activations/cone/storage.rs` - ConeStorage implementation using direct ArborStorage
- HANDLE-9: Handle resolution via Plexus (for cross-plugin resolution)
