# Stream-First Hub: What We Have, What We Need

## The Goal

```rust
#[hub_methods]
impl BashActivation {
    /// Execute a command
    async fn execute(&self, command: String) -> impl Stream<Item = BashEvent> { ... }

    /// Get status (non-streaming)
    async fn status(&self) -> StatusResponse { ... }
}
```

This single impl block should generate:
1. **RPC server** - jsonrpsee trait + impl with subscriptions for streams
2. **Method schema** - JSON Schema for inputs, outputs, stream items
3. **Runtime introspection** - `GET /schema` returns full type info
4. **Dispatch enum** - For routing and serialization

## What We Have Now

### 1. Schema Extraction (working)
```rust
// Any type with JsonSchema can be schematized
let schema = schemars::schema_for!(BashEvent);

// Stream items with terminal markers
trait SessionItem {
    fn is_terminal(&self) -> bool;
}
```

### 2. Stream Item Enums (working)
```rust
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type")]
enum BashEvent {
    Stdout { line: String },
    Stderr { line: String },
    Exit { code: i32 },  // terminal
}
```

### 3. Protocol Validation (working)
```rust
ValidatingStream::new(stream)  // Catches missing terminal, items after terminal
```

## What's Missing

### 1. `#[hub_methods]` Macro Core

Parse impl block, for each method extract:
- Method name
- Input types (from fn args)
- Output type (return type)
- Is it streaming? (check for `impl Stream<Item = T>`)

```rust
struct MethodInfo {
    name: String,
    inputs: Vec<(String, Type)>,
    output: OutputType,
}

enum OutputType {
    Value(Type),           // -> T
    Stream(Type),          // -> impl Stream<Item = T>
}
```

### 2. Generated Method Enum

```rust
// Generated from impl block
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(tag = "method", content = "params")]
enum BashMethod {
    Execute { command: String },
    Status,
}
```

### 3. Generated Event Union

```rust
// All possible outputs from this activation
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(tag = "source", content = "event")]
enum BashOutput {
    Execute(BashEvent),      // Stream items
    Status(StatusResponse),  // Single response
}
```

### 4. RPC Trait Generation

```rust
// Generated trait with jsonrpsee attributes
#[rpc(server, namespace = "bash")]
trait BashRpc {
    #[subscription(name = "execute", item = BashEvent)]
    async fn execute(&self, command: String) -> SubscriptionResult;

    #[method(name = "status")]
    async fn status(&self) -> RpcResult<StatusResponse>;
}

// Generated impl
impl BashRpcServer for BashActivation { ... }
```

### 5. Schema Endpoint

```rust
impl BashActivation {
    fn schema() -> ActivationSchema {
        ActivationSchema {
            name: "bash",
            methods: vec![
                MethodSchema {
                    name: "execute",
                    input_schema: schema_for!(ExecuteParams),
                    output: OutputSchema::Stream {
                        item_schema: schema_for!(BashEvent),
                        terminal_variants: vec!["exit"],
                    },
                },
                MethodSchema {
                    name: "status",
                    input_schema: schema_for!(()),
                    output: OutputSchema::Value {
                        schema: schema_for!(StatusResponse),
                    },
                },
            ],
        }
    }
}
```

## The Macro Strategy

### Input
```rust
#[hub_methods(namespace = "bash")]
impl BashActivation {
    async fn execute(&self, command: String) -> impl Stream<Item = BashEvent> { ... }
    async fn status(&self) -> StatusResponse { ... }
}
```

### Output (generated)

```rust
// 1. Method dispatch enum
#[derive(Serialize, Deserialize, JsonSchema)]
pub enum BashMethod { Execute { command: String }, Status }

// 2. Output union enum
#[derive(Serialize, Deserialize, JsonSchema)]
pub enum BashOutput { Execute(BashEvent), Status(StatusResponse) }

// 3. RPC trait
#[rpc(server, namespace = "bash")]
pub trait BashRpc { ... }

// 4. RPC impl
impl BashRpcServer for BashActivation { ... }

// 5. Schema
impl BashActivation {
    pub fn activation_schema() -> ActivationSchema { ... }
}

// 6. Original impl (preserved)
impl BashActivation {
    async fn execute(&self, command: String) -> impl Stream<Item = BashEvent> { ... }
    async fn status(&self) -> StatusResponse { ... }
}
```

## Stream Item Requirements

For streaming methods, the item type should implement:

```rust
trait StreamEvent: Serialize + DeserializeOwned + JsonSchema {
    /// Is this the final event in the stream?
    fn is_terminal(&self) -> bool;
}
```

This can be derived:
```rust
#[derive(StreamEvent, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type")]
enum BashEvent {
    Stdout { line: String },
    Stderr { line: String },
    #[terminal]
    Exit { code: i32 },
}
```

## Runtime Type Exposure

The hub becomes self-describing:

```
GET /schema
{
  "activations": {
    "bash": {
      "methods": {
        "execute": {
          "params": { "type": "object", "properties": { "command": { "type": "string" } } },
          "returns": { "stream": true, "item": { "oneOf": [...] }, "terminal": ["exit"] }
        },
        "status": {
          "params": {},
          "returns": { "stream": false, "schema": { ... } }
        }
      }
    }
  }
}
```

## Implementation Order

1. **`#[derive(StreamEvent)]`** - Generate `is_terminal()` from `#[terminal]` attribute
2. **Method parsing** - Extract method info from impl block AST
3. **Enum generation** - Create Method and Output enums
4. **Schema generation** - Create `activation_schema()` function
5. **RPC generation** - Create jsonrpsee trait and impl
6. **Hub integration** - Wire schema endpoint, registration

## Key Insight

The impl block IS the source of truth. Everything else is derived:

```
impl BashActivation { methods... }
         ↓
    ┌────┴────┐
    ↓         ↓
 Schema    RPC Trait
    ↓         ↓
    └────┬────┘
         ↓
   Runtime Introspection
```

No separate trait definition. No manual schema writing. The types you write ARE the API.
