# Capability Boundary Documentation
This document defines the trait ownership and migration guide for converge-core's
capability boundary traits. These traits form the abstraction layer between
converge-core (correctness axioms) and capability crates (implementations).
## Trait Ownership Table
| `CapabilityError` | `converge-core` | `traits::error` | Error classification interface | N/A |
| `ErrorCategory` | `converge-core` | `traits::error` | Error category enumeration | N/A |
| `ChatBackend` | `converge-core` | `traits::llm` | Chat completion (GAT async) | No |
| `EmbedBackend` | `converge-core` | `traits::llm` | Embedding generation (GAT async) | No |
| `LlmBackend` | `converge-core` | `traits::llm` | Chat + Embed umbrella | No |
| `DynChatBackend` | `converge-core` | `traits::llm` | Dyn-safe chat wrapper | Yes |
| `DynEmbedBackend` | `converge-core` | `traits::llm` | Dyn-safe embed wrapper | Yes |
| `RecallReader` | `converge-core` | `traits::recall` | Query-only recall access | No |
| `RecallWriter` | `converge-core` | `traits::recall` | Mutation recall access | No |
| `Recall` | `converge-core` | `traits::recall` | Reader + Writer umbrella | No |
| `DynRecallReader` | `converge-core` | `traits::recall` | Dyn-safe recall reader | Yes |
| `ExperienceAppender` | `converge-core` | `traits::store` | Append-only event storage | No |
| `ExperienceReplayer` | `converge-core` | `traits::store` | Streaming replay access | No |
| `DynExperienceAppender` | `converge-core` | `traits::store` | Dyn-safe appender | Yes |
| `DynExperienceReplayer` | `converge-core` | `traits::store` | Dyn-safe replayer | Yes |
| `Validator` | `converge-core` | `traits::validator` | Proposal validation | No |
| `DynValidator` | `converge-core` | `traits::validator` | Dyn-safe validator | Yes |
| `Promoter` | `converge-core` | `traits::promoter` | Proposal promotion | No |
| `DynPromoter` | `converge-core` | `traits::promoter` | Dyn-safe promoter | Yes |
| `Executor` | `converge-core` | `traits` (inline) | Parallel execution strategy | No |
| `Randomness` | `converge-core` | `traits` (inline) | Random number generation | No |
| `Fingerprint` | `converge-core` | `traits` (inline) | Cryptographic hashing | No |
## Deprecated Traits
| `LlmProvider` | `ChatBackend`, `EmbedBackend` | `llm.rs` -> `traits::llm` |
| `LlmBackend` (backend.rs) | `traits::LlmBackend` | `backend.rs` -> `traits::llm` |
| `ExperienceStore` | `ExperienceAppender`, `ExperienceReplayer` | `experience_store.rs` -> `traits::store` |
## Design Principles
### 1. Split by Capability, Not Provider
Traits are split by capability (chat, embed, recall, store) rather than provider
(OpenAI, Anthropic). This allows:
- Fine-grained authorization boundaries
- Different implementations for different capabilities
- Mixing providers per capability
### 2. GAT Async Pattern
All async traits use Generic Associated Types (GATs) for zero-cost async:
```rust
pub trait ChatBackend: Send + Sync {
type ChatFut<'a>: Future<Output = Result<ChatResponse, LlmError>> + Send + 'a
where
Self: 'a;
fn chat<'a>(&'a self, req: ChatRequest) -> Self::ChatFut<'a>;
}
```
This avoids the overhead of `async_trait` proc macros and box allocation.
### 3. Dyn-Safe Wrappers
For runtime polymorphism (`dyn Trait`), use the `Dyn*` variants:
```rust
// Static dispatch (zero-cost, compile-time)
fn process<B: ChatBackend>(backend: &B, req: ChatRequest) { ... }
// Dynamic dispatch (runtime, box allocation)
fn process_dyn(backend: &dyn DynChatBackend, req: ChatRequest) { ... }
```
Blanket implementations automatically provide `Dyn*` for any `*` implementor.
### 4. Error Classification
All capability errors implement `CapabilityError` for uniform handling:
```rust
impl CapabilityError for LlmError {
fn category(&self) -> ErrorCategory { ... }
fn is_transient(&self) -> bool { ... }
fn is_retryable(&self) -> bool { ... }
fn retry_after(&self) -> Option<Duration> { ... }
}
```
This enables generic retry/circuit breaker logic across all capabilities.
## Migration Guide
### From `LlmProvider` to `ChatBackend`
**Before (deprecated):**
```rust
use converge_core::llm::{LlmProvider, LlmRequest, LlmResponse, LlmError};
impl LlmProvider for MyProvider {
fn name(&self) -> &str { "my-provider" }
fn model(&self) -> &str { "my-model" }
fn complete(&self, request: &LlmRequest) -> Result<LlmResponse, LlmError> {
// Synchronous implementation
}
}
```
**After (recommended):**
```rust
use converge_core::traits::{ChatBackend, ChatRequest, ChatResponse, LlmError};
use std::future::Future;
impl ChatBackend for MyProvider {
type ChatFut<'a> = impl Future<Output = Result<ChatResponse, LlmError>> + Send + 'a
where
Self: 'a;
fn chat<'a>(&'a self, req: ChatRequest) -> Self::ChatFut<'a> {
async move {
// Async implementation
}
}
}
```
### From `LlmBackend` (backend.rs) to `traits::LlmBackend`
**Before (deprecated):**
```rust
use converge_core::backend::{LlmBackend, BackendRequest, BackendResponse, BackendResult};
impl LlmBackend for MyBackend {
fn name(&self) -> &str { "my-backend" }
fn supports_replay(&self) -> bool { false }
fn execute(&self, request: &BackendRequest) -> BackendResult<BackendResponse> {
// Synchronous implementation
}
fn supports_capability(&self, cap: BackendCapability) -> bool { ... }
}
```
**After (recommended):**
```rust
use converge_core::traits::{ChatBackend, EmbedBackend, LlmBackend};
// Implement ChatBackend and EmbedBackend separately
// LlmBackend is automatically implemented via blanket impl
impl ChatBackend for MyBackend { ... }
impl EmbedBackend for MyBackend { ... }
// MyBackend now implements LlmBackend automatically
```
### From `ExperienceStore` to `ExperienceAppender`/`ExperienceReplayer`
**Before (deprecated):**
```rust
use converge_core::experience_store::{ExperienceStore, ExperienceEventEnvelope, EventQuery};
impl ExperienceStore for MyStore {
fn append_event(&self, event: ExperienceEventEnvelope) -> ExperienceStoreResult<()> { ... }
fn query_events(&self, query: &EventQuery) -> ExperienceStoreResult<Vec<ExperienceEventEnvelope>> { ... }
// ...other methods
}
```
**After (recommended):**
```rust
use converge_core::traits::{ExperienceAppender, ExperienceReplayer, StoreError};
// Split into separate traits by capability
impl ExperienceAppender for MyStore {
type AppendFut<'a> = impl Future<Output = Result<(), StoreError>> + Send + 'a
where
Self: 'a;
fn append<'a>(&'a self, event: ExperienceEventEnvelope) -> Self::AppendFut<'a> {
async move { ... }
}
}
impl ExperienceReplayer for MyStore {
type ReplayFut<'a> = impl Future<Output = Result<ReplayBatch, StoreError>> + Send + 'a
where
Self: 'a;
fn replay<'a>(&'a self, cursor: ReplayCursor, options: ReplayOptions) -> Self::ReplayFut<'a> {
async move { ... }
}
}
```
## Type-State Integration (Validator/Promoter)
The `Validator` and `Promoter` traits integrate with the type-state pattern:
```rust
// Validator: Draft -> ValidationReport
impl Validator for MyValidator {
type ValidateFut<'a> = impl Future<Output = Result<ValidationReport, ValidatorError>> + Send + 'a
where
Self: 'a;
fn validate<'a>(
&'a self,
proposal: &'a Proposal<Draft>,
policy: &'a ValidationPolicy,
) -> Self::ValidateFut<'a> { ... }
}
// Promoter: Validated -> Fact
impl Promoter for MyPromoter {
type PromoteFut<'a> = impl Future<Output = Result<Fact, PromoterError>> + Send + 'a
where
Self: 'a;
fn promote<'a>(
&'a self,
proposal: Proposal<Validated>,
report: &'a ValidationReport,
context: &'a PromotionContext,
) -> Self::PromoteFut<'a> { ... }
}
```
The type system enforces the lifecycle: Draft -> Validated -> Fact.
## Version History
- **v0.2.0**: Added capability boundary traits (`traits/` module)
- `ChatBackend`, `EmbedBackend`, `LlmBackend` (replaces `LlmProvider`, `backend::LlmBackend`)
- `RecallReader`, `RecallWriter`, `Recall` (new split design)
- `ExperienceAppender`, `ExperienceReplayer` (replaces `ExperienceStore`)
- `Validator`, `Promoter` (new, type-state aware)
- All traits use GAT async pattern
- Dyn-safe wrappers for all traits
- **v0.1.0**: Original synchronous traits
- `LlmProvider` (deprecated)
- `LlmBackend` in backend.rs (deprecated)
- `ExperienceStore` (deprecated)