Trait-Driven Design Pattern
ZeroClaw’s architecture is built on Rust traits, which define explicit extension points for swappable components. This design provides compile-time guarantees, type safety, and clear boundaries between subsystems.Why Traits?
FromAGENTS.md §2:
Trait + factory architecture is the stability backboneBenefits:
- Extension points are intentionally explicit and swappable
- Most features should be added via trait implementation + factory registration, not cross-cutting rewrites
- Compile-Time Safety: Invalid implementations won’t compile
- Explicit Contracts: Trait signatures document what each component must provide
- Zero-Cost Abstraction: Trait dispatch is optimized away in release builds
- Testability: Easy to create mock implementations for testing
- Parallel Development: Multiple implementations can be developed independently
Core Traits Overview
| Trait | Module | Purpose |
|---|---|---|
Provider | src/providers/traits.rs | Model inference backends |
Channel | src/channels/traits.rs | Messaging platform integrations |
Tool | src/tools/traits.rs | Agent capabilities (shell, files, etc.) |
Memory | src/memory/traits.rs | Persistence backends |
Sandbox | src/security/traits.rs | OS-level isolation |
Peripheral | src/peripherals/traits.rs | Hardware board interfaces |
RuntimeAdapter | src/runtime/traits.rs | Execution environment adapters |
Observer | src/observability/traits.rs | Telemetry and observability |
Provider Trait
TheProvider trait defines the interface for LLM backends:
Provider Capabilities
Providers declare their capabilities to enable intelligent adaptation:Channel Trait
TheChannel trait defines the interface for messaging platforms:
Tool Trait
TheTool trait defines agent capabilities:
Memory Trait
TheMemory trait defines persistence backends:
Factory Pattern
Traits are instantiated via factory functions that map string keys to implementations:Provider Factory
Channel Factory
Tool Factory
Adding New Implementations
To add a new component:- Implement the trait in a new submodule
- Register in factory function with a stable key
- Add tests for factory wiring and core behavior
- Update docs reference (e.g.,
providers-reference.md)
AGENTS.md §7.1:
Best Practices
Trait Implementation
- Keep default methods simple: Use conservative defaults
- Document behavior: Trait docs should explain contracts
- Handle errors explicitly: Return
anyhow::Resultwith context - Avoid blocking: Use
asyncfor I/O operations
Factory Registration
- Use stable keys: Factory keys are user-facing (“openai”, “telegram”)
- Handle aliases internally: Don’t expose implementation details
- Validate early: Check config before constructing
- Fail fast: Return errors during factory construction
Dependency Injection
- Pass Arc<T> for shared state:
Arc<SecurityPolicy>,Arc<dyn Memory> - Clone Arc, not data:
Arc::clone(&security)is cheap - Use trait objects:
Arc<dyn Trait>for polymorphism