# CLAUDE.md — Rust Project Conventions
## Philosophy
This project follows **functional**, **type-driven**, and **domain-driven** design principles. The type system is the primary tool for correctness. If a state is illegal, it should be unrepresentable.
## Architecture
- **Modules by domain context**, not by technical layer. No `controllers/`, `services/`, `models/` splits.
- Each bounded context gets its own module with its own types. Cross-context communication happens through well-defined public interfaces.
- Prefer thin `main.rs`/`lib.rs` that wires contexts together.
## Types
- **Newtypes** for all domain primitives. Never pass raw `String`, `u64`, `f64`, etc. across function boundaries when they carry domain meaning.
- **Sum types (enums)** to model domain variants and state machines. Prefer enums over booleans or stringly-typed fields.
- **Phantom types** where compile-time state tracking is warranted.
- **No public struct fields**. All fields must be private. Expose access through getter methods and construct through associated functions or builder patterns.
- `#[must_use]` on pure functions and types whose values should not be silently dropped.
## Error Handling
- A single project-wide `Error` enum defined in a dedicated `error` module.
- Each variant wraps an underlying error type from a dependency or domain context.
- Implement `From<UnderlyingError> for Error` for every variant to enable `?` ergonomics throughout the codebase.
- Implement `std::fmt::Display` and `std::error::Error` by hand, no `thiserror`, no `anyhow`.
- Domain logic returns `Result<T, Error>`. Never panic in library code.
## Style
- **Prefer `match` over `if`/`else`**. The only exception is when branching on a `bool` value, where `if`/`else` is preferred.
- **No `return` keyword**. Every function body is a single expression. Use `?` for early error propagation and combinators for control flow.
- **No `mut`**. All bindings and parameters must be immutable.
- **Combinators** (`map`, `and_then`, `filter`, `fold`, etc.) over imperative loops and match-and-early-return.
- **Never match on `Option<_>`**. Use combinators instead.
- **Prefer combinators on `Result<_, _>`**. Reserve `match` only when the arm logic is too complex for a chain.
- **No `unwrap()`/`expect()`**. Prohibited everywhere, including tests.
- **No `loop` or `for`**. Use iterator combinators or recursion.
- **No `scan`**. Use `successors`, `fold`, or recursion instead.
- **No reference counting** (`Rc`, `Arc`).
- **No naked `as` casts**. Use `From`/`Into` for infallible conversions and `TryFrom`/`TryInto` for fallible ones.
- **No panicking indexing**. Use `.get()` and handle the `None` case explicitly.
## Traits
- Trait definitions follow domain boundaries.
- **No dynamic polymorphism**. Never use `dyn Trait` or trait objects (except where required by std traits like `std::error::Error::source`).
- **Implement the standard trait** (`Add`, `Display`, etc.) instead of ad-hoc methods.
## Linting
- **Always run `RUSTFLAGS="-D warnings" cargo clippy`** before considering code complete.
## Testing
- Property-based tests via `proptest` where types suggest invariants.
- Unit tests live in the same file as the code they test (`#[cfg(test)]` modules).
- Test names describe the property or behavior, not the function name.
## Dependencies
- **field-cat** provides the `Field` and `FieldBytes` traits and concrete fields.
- **sha2** provides SHA-256 for the Fiat-Shamir transcript and Merkle tree.
- Minimize other dependencies. No `thiserror`, no `anyhow`.
- **Never use path dependencies** in published-library Cargo.toml. All deps are plain crates.io version specifiers.
## Documentation
- Doc comments on all public items. Module-level `//!` on `lib.rs` and major modules.
- `# Examples` sections with runnable doctests on key public APIs.
- No `unwrap()`, `expect()`, or `unreachable!()` in any documentation or doctest.
- Use backtick links to cross-reference types.
- Docs auto-publish via `.github/workflows/docs.yml` to GitHub Pages.