# Generic HubContext Implementation

**Status**: Complete
**Branch**: `feature/generic-hub-context` (hub-core, substrate)

## Goal

Make parent→child context passing generic and symmetric. Instead of plugins hardcoding `Weak<Plexus>`, they become generic over `P: HubContext`:

```rust
// Before
pub struct Cone {
    hub: Arc<OnceLock<Weak<Plexus>>>,  // Hardcoded
}

// After
pub struct Cone<P: HubContext = NoParent> {
    hub: Arc<OnceLock<P>>,  // Generic
}
```

This allows any hub to pass whatever context it wants to its children.

## Completed

### 1. hub-core changes (DONE)

**File**: `hub-core/src/plexus/hub_context.rs` (NEW)

Created `HubContext` trait:
```rust
#[async_trait]
pub trait HubContext: Clone + Send + Sync + 'static {
    async fn resolve_handle(&self, handle: &Handle) -> Result<PlexusStream, PlexusError>;
    async fn call(&self, method: &str, params: Value) -> Result<PlexusStream, PlexusError>;
    fn is_valid(&self) -> bool { true }
}
```

Also created:
- `ParentAware` trait - marker for plugins that accept parent injection
- `NoParent` struct - no-op context for standalone/testing

**File**: `hub-core/src/plexus/mod.rs`

Added module and re-exports:
```rust
pub mod hub_context;
pub use hub_context::{HubContext, ParentAware, NoParent};
```

**File**: `hub-core/src/plexus/plexus.rs`

Added `HubContext` impl for `Weak<Plexus>`:
```rust
#[async_trait]
impl HubContext for Weak<Plexus> {
    async fn resolve_handle(&self, handle: &Handle) -> Result<PlexusStream, PlexusError> {
        let plexus = self.upgrade().ok_or_else(|| ...)?;
        plexus.do_resolve_handle(handle).await
    }
    // ... call() and is_valid() also implemented
}
```

**hub-core compiles successfully** ✓

### 2. Cone changes (IN PROGRESS)

**File**: `substrate/src/activations/cone/activation.rs`

Partially updated:

```rust
// Struct is now generic
pub struct Cone<P: HubContext = NoParent> {
    storage: Arc<ConeStorage>,
    llm_registry: Arc<ModelRegistry>,
    hub: Arc<OnceLock<P>>,
    _phantom: PhantomData<P>,
}

// Generic impl for all P
impl<P: HubContext> Cone<P> {
    pub async fn with_context_type(...) -> Result<Self, String> { ... }
    pub fn inject_parent(&self, parent: P) { ... }
    pub fn has_parent(&self) -> bool { ... }
    pub fn parent(&self) -> Option<&P> { ... }
}

// Convenience for NoParent (standalone)
impl Cone<NoParent> {
    pub async fn new(...) -> Result<Self, String> { ... }
}
```

## Completed Work

### 3. hub-macro updated for generic impl blocks

Updated `hub-macro/src/codegen/` to preserve impl generics:
- Pass `impl_generics`, `self_ty`, and `where_clause` through to generated code
- Generated trait impls now use full generic form: `impl<P: HubContext> Activation for Cone<P>`

### 4. Cone and ClaudeCode updated

Both plugins now:
- Generic over `P: HubContext = NoParent`
- Have `inject_parent(P)` method instead of `inject_hub(Weak<Plexus>)`
- Keep convenience `new()` for `NoParent` variant (standalone/testing)
- Use `with_context_type()` for explicit parent type

### 5. builder.rs uses Arc::new_cyclic

```rust
let cone: Cone<Weak<Plexus>> = Cone::with_context_type(...).await?;
let claudecode: ClaudeCode<Weak<Plexus>> = ClaudeCode::with_context_type(...);

let plexus = Arc::new_cyclic(|weak_plexus: &Weak<Plexus>| {
    cone.inject_parent(weak_plexus.clone());
    claudecode.inject_parent(weak_plexus.clone());

    Plexus::new()
        .register(cone)
        .register(claudecode)
        // ...
});
```

## Key Files

| File | Status |
|------|--------|
| `hub-core/src/plexus/hub_context.rs` | ✓ Created |
| `hub-core/src/plexus/mod.rs` | ✓ Updated |
| `hub-core/src/plexus/plexus.rs` | ✓ Updated |
| `hub-macro/src/codegen/mod.rs` | ✓ Updated |
| `hub-macro/src/codegen/activation.rs` | ✓ Updated |
| `substrate/src/activations/cone/activation.rs` | ✓ Updated |
| `substrate/src/activations/claudecode/activation.rs` | ✓ Updated |
| `substrate/src/builder.rs` | ✓ Updated |

## Design Decisions

1. **Default type parameter**: `Cone<P: HubContext = NoParent>` allows `Cone` to work without explicit type in simple cases

2. **PhantomData**: Added `_phantom: PhantomData<P>` since P isn't directly stored (it's in OnceLock)

3. **Separate constructors**: `with_context_type()` for generic, `new()` convenience for NoParent

4. **Trait methods over concrete**: `inject_parent(P)` instead of `inject_hub(Weak<Plexus>)`

## Testing

After completion, verify:
```bash
cd hub-core && cargo check
cd substrate && cargo check
cd substrate && cargo test
```
