# CLAUDE.md Rust TUI coding agent. Ratatui + Crossterm + Tokio. See DESIGN.md for architecture decisions and PLAN.md for implementation phases. ## Commands - `cargo build`: Build the project - `cargo test`: Run all unit and integration tests - `cargo test --lib`: Unit tests only - `cargo test --test '*'`: Integration tests only - `cargo clippy -- -D warnings`: Lint (must pass with zero warnings) - `cargo fmt --check`: Format check - `cargo run -- --project-dir `: Run against a project directory ## Architecture Six modules with strict boundaries: - `src/app/` -- Wiring, lifecycle, tokio runtime setup - `src/tui/` -- Ratatui rendering, input handling, vim modes. Communicates with core ONLY via channels (`UserAction` -> core, `UIEvent` <- core). Never touches conversation state directly. - `src/core/` -- Conversation tree, orchestrator loop, sub-agent lifecycle - `src/provider/` -- `ModelProvider` trait + Claude implementation. Leaf module, no internal dependencies. - `src/tools/` -- `Tool` trait, registry, built-in tools. Depends only on `sandbox`. - `src/sandbox/` -- Landlock policy, path validation, command execution. Leaf module. - `src/session/` -- JSONL logging, session read/write. Leaf module. The channel boundary between `tui` and `core` is critical -- never bypass it. The TUI is a frontend; core is the engine. This separation enables headless mode for benchmarking. ## Code Style - Use `thiserror` for error types, not `anyhow` in library code (`anyhow` only in `main.rs`/`app`) - Prefer `impl Trait` return types over boxing when possible - All public types need doc comments - No `unwrap()` in non-test code -- use `?` or explicit error handling - Async functions should be cancel-safe where possible - Use `tracing` for structured logging, not `println!` or `log` ## Documentation Prefer a literate style: doc comments should explain *why* and *how*, not just restate the signature. Use only characters available on a standard US QWERTY keyboard in all doc comments and inline comments. Specifically: - Use `->` and `<-` instead of Unicode arrow glyphs - Use `--` instead of em dashes or en dashes - Use `+`, `-`, `|` for ASCII box diagrams instead of Unicode box-drawing characters - Use `...` instead of the ellipsis character - Spell out "Section N.N" instead of the section-sign glyph When a function or type implements an external protocol or spec: - Document the relevant portion of the protocol inline (packet shapes, event sequences, state machines) - Link to the authoritative external source -- API reference, RFC, WHATWG spec, etc. - Include a mapping table or lifecycle diagram when there are multiple cases to distinguish For example, `run_stream` in `src/provider/claude.rs` documents the full SSE event sequence in a text diagram and links to both the Anthropic streaming reference and the WHATWG SSE spec. Aim for that level of context in any code that speaks a wire format or external API. ## Conversation Data Model Events use parent IDs forming a tree (not a flat list). This enables future branching. Every event has: id, parent_id, timestamp, event_type, token_usage. A "turn" is all events between two user messages -- this is the unit for token tracking. ## Testing - Unit tests go in the same file as the code (`#[cfg(test)] mod tests`) - Integration tests go in `tests/` - TUI widget tests use `ratatui::backend::TestBackend` - Provider tests replay recorded SSE fixtures from `tests/fixtures/` - Sandbox tests use `tempdir` and skip Landlock-specific assertions if kernel < 5.13 - Run `cargo test` before every commit ## Key Constraints - All file I/O and process spawning in tools MUST go through `Sandbox` -- never use `std::fs` or `std::process::Command` directly in tool implementations - The `ModelProvider` trait must remain provider-agnostic -- no Claude-specific types in the trait interface - Session JSONL is append-only. Never rewrite history. Branching works by writing new events with different parent IDs. - Token usage must be tracked per-event and aggregatable per-turn ## Do Not - Add MCP support (deferred, but keep tool trait compatible) - Use `unsafe` without discussion - Add dependencies without checking if an existing dep covers the use case - Modify test fixtures without re-recording from a real API session