# Self-Documenting RPC Layer

## Problem

When a client sends a malformed request or calls an unknown method, the current error response provides no guidance. The client has no way to discover how to interact with the system.

## Design Principle

**Errors should point, not dump.** Every error directs the user toward discovery, but doesn't bloat the response with the full schema. The system teaches by guiding, not by overwhelming.

## Layer Separation

```
┌─────────────────────────────────────────────────────────────┐
│  Substrate (Communication Layer)                            │
│  - Speaks JSON-RPC 2.0 over WebSocket                       │
│  - Handles parse errors, protocol guidance                  │
│  - Knows HOW to communicate                                 │
├─────────────────────────────────────────────────────────────┤
│  Plexus (Business Logic Layer)                              │
│  - Routes to activations                                    │
│  - Knows WHAT is available (schema, methods)                │
│  - No knowledge of wire format                              │
├─────────────────────────────────────────────────────────────┤
│  Activations (Capabilities)                                 │
│  - health, bash, arbor, cone                                │
│  - Pure domain logic                                        │
└─────────────────────────────────────────────────────────────┘
```

**Key insight**: The plexus is ON the substrate. The substrate makes communication possible. Therefore:
- **Substrate** provides the interpretability layer (how to talk)
- **Plexus** provides the discoverability layer (what to say)

## Guidance Behavior

**Default: Help enabled.** Errors include guidance hints. Can be disabled via configuration for minimal responses.

## Error Handling Strategy

Errors provide **contextual help** based on how close the request was to being valid - just like CLI help:

```
CLI analogy:
  $ git foo           → "git: 'foo' is not a git command. See 'git --help'"
  $ git commit --foo  → Shows commit usage with valid options

RPC analogy:
  foo_bar             → "Activation 'foo' not found. Available: arbor, bash, cone, health"
  bash_foo            → "Method 'foo' not found in bash. Available: execute"
  bash_execute        → "Missing 'command'. Usage: bash_execute [command: string]"
```

### Parse Error (Non-JSON-RPC Request)

If substrate receives ANY request that isn't valid JSON-RPC:

```json
{
  "jsonrpc": "2.0",
  "id": null,
  "error": {
    "code": -32700,
    "message": "Parse error: This server speaks JSON-RPC 2.0 over WebSocket",
    "data": {
      "try": {"jsonrpc": "2.0", "id": 1, "method": "plexus_schema", "params": []}
    }
  }
}
```

### Activation Not Found

When the activation namespace doesn't exist, show available activations:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Activation 'foo' not found",
    "data": {
      "available_activations": ["arbor", "bash", "cone", "health"],
      "try": {"jsonrpc": "2.0", "id": 1, "method": "plexus_schema", "params": []}
    }
  }
}
```

### Method Not Found (Activation Exists)

When the activation exists but method doesn't, show methods for that activation:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method 'foo' not found in activation 'bash'",
    "data": {
      "activation": "bash",
      "available_methods": ["execute"],
      "try": {"jsonrpc": "2.0", "id": 1, "method": "bash_execute", "params": ["echo hello"]}
    }
  }
}
```

### Invalid Params (Method Exists)

When method exists but params are wrong, show the method's signature:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Invalid params for bash_execute: missing 'command'",
    "data": {
      "method": "bash_execute",
      "usage": "bash_execute [command: string]",
      "description": "Execute shell command, stream output",
      "example": {"jsonrpc": "2.0", "id": 1, "method": "bash_execute", "params": ["echo hello"]}
    }
  }
}
```

## The `try` Pattern

Every guided error includes a `data.try` field containing the exact JSON-RPC request to make next. This is:
- Copy-pasteable
- Machine-readable (clients can auto-retry with suggested request)
- Minimal (just one request, not the whole schema)

## Flow

```
Client sends garbage
       │
       ▼
┌──────────────────────────────────────────────┐
│ Substrate: "I speak JSON-RPC 2.0. Try this:" │
│ {"jsonrpc":"2.0","id":1,"method":            │
│  "plexus_schema","params":[]}                │
└──────────────────────────────────────────────┘
       │
       ▼
Client sends plexus_schema request
       │
       ▼
┌──────────────────────────────────────────────┐
│ Plexus: Returns all activations and methods  │
│ {activations: [...], total_methods: N}       │
└──────────────────────────────────────────────┘
       │
       ▼
Client now knows what to call
```

## Implementation

### Substrate Layer

The substrate layer (main.rs / server setup) handles protocol-level errors:

```rust
// Conceptual - actual implementation depends on jsonrpsee hooks
fn on_parse_error() -> JsonRpcError {
    JsonRpcError {
        code: -32700,
        message: "Parse error: This server speaks JSON-RPC 2.0 over WebSocket",
        data: json!({
            "try": {"jsonrpc": "2.0", "id": 1, "method": "plexus_schema", "params": []}
        }),
    }
}
```

### Plexus Layer

The plexus returns semantic errors without RPC knowledge:

```rust
// PlexusError stays pure - no RPC format
pub enum PlexusError {
    ActivationNotFound(String),
    MethodNotFound { activation: String, method: String },
    InvalidParams(String),
    ExecutionError(String),
}
```

### RPC Wrapper

A thin wrapper converts PlexusError to guided JSON-RPC errors:

```rust
fn plexus_error_to_rpc(err: PlexusError, help_enabled: bool) -> JsonRpcError {
    let (code, message) = match &err {
        PlexusError::MethodNotFound { .. } => (-32601, err.to_string()),
        PlexusError::InvalidParams(msg) => (-32602, format!("Invalid params: {}", msg)),
        // ...
    };

    let data = if help_enabled {
        Some(json!({
            "hint": "Call plexus_schema to discover available methods",
            "try": {"jsonrpc": "2.0", "id": 1, "method": "plexus_schema", "params": []}
        }))
    } else {
        None
    };

    JsonRpcError { code, message, data }
}
```

## Configuration

```rust
struct SubstrateConfig {
    /// Include help hints in error responses (default: true)
    error_guidance: bool,
}
```

When `error_guidance: false`, errors are minimal:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method not found: foo_bar"
  }
}
```

## Summary

### Layer Responsibilities

| Layer | Responsibility | Example |
|-------|---------------|---------|
| Substrate | Protocol guidance ("how to talk") | Parse error → try plexus_schema |
| Plexus | Method discovery ("what to say") | plexus_schema → all methods |
| Activations | Domain logic | cone_registry → model list |

### Progressive Disclosure

| Error Level | What's Wrong | Help Shows |
|-------------|--------------|------------|
| Parse error | Can't understand request | Protocol format + plexus_schema example |
| Activation not found | `foo_*` - no such activation | List of available activations |
| Method not found | `bash_foo` - no such method | Methods available in `bash` |
| Invalid params | `bash_execute` - wrong args | Method signature + example |

The system is self-documenting through **contextual, progressive help** - the closer you get to a valid request, the more specific the guidance becomes.

## Related

- [Dynamic CLI Architecture](../../../symbols/docs/architecture/16681001483371084543_dynamic-cli-architecture.md)
- [Substrate RPC Protocol](../../../symbols/docs/architecture/16681062792662575615_substrate-rpc-protocol.md)
