Skip to main content

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?

From AGENTS.md §2:
Trait + factory architecture is the stability backbone
  • Extension points are intentionally explicit and swappable
  • Most features should be added via trait implementation + factory registration, not cross-cutting rewrites
Benefits:
  • 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

Provider Trait

The Provider trait defines the interface for LLM backends:

Provider Capabilities

Providers declare their capabilities to enable intelligent adaptation:
Example - OpenAI Provider:
See Providers for complete details.

Channel Trait

The Channel trait defines the interface for messaging platforms:
Example - Telegram Channel:
See Channels for complete details.

Tool Trait

The Tool trait defines agent capabilities:
Example - Shell Tool:
See Tools for complete details.

Memory Trait

The Memory trait defines persistence backends:
Example - Markdown Memory:
See Memory for complete details.

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:
  1. Implement the trait in a new submodule
  2. Register in factory function with a stable key
  3. Add tests for factory wiring and core behavior
  4. Update docs reference (e.g., providers-reference.md)
Example workflow from 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::Result with context
  • Avoid blocking: Use async for 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

Next Steps

  • Providers - Provider system deep dive
  • Channels - Channel system architecture
  • Tools - Tool system and security
  • Memory - Memory backends and persistence