Guidance for AI assistants (Claude Code, Gemini, Codex, etc.) collaborating on this repository.
Read this file first, then load the relevant skills for your current task. project-structure.md, development-commands.md, code-style.md, tests.md, and defensive-programming.md are the default set for most Core work. Load error-handling.md when touching error surfaces or JSON access, architecture.md when changing provider/repository/UniFFI patterns, common-issues.md when debugging tricky failures, and swapper-checklist.md only for swapper integrations.
- Project Structure — Repo layout, crates, and tech stack
- Development Commands — Build, test, lint, format, mobile
- Code Style — Formatting, naming, imports, code organization
- Error Handling — Error types, propagation, JSON access
- Architecture — Provider/mapper, repository, RPC, UniFFI patterns
- Tests — Test conventions, mocks, integration tests
- Defensive Programming — Safety rules and exhaustive patterns
- Common Issues — Known anti-patterns and their fixes
- Swapper Checklist — Integration checklist for swapper providers
- State assumptions explicitly. UniFFI bounds, lifetimes, provider trait contracts, and JSON shape assumptions are invisible — call them out so a reviewer can spot the wrong one
- Read before you write. Open the file's existing exports, the immediate caller, the related provider/mapper/repository, and any obvious testkit fixture before adding code. "Looks orthogonal to me" is the most expensive sentence in this crate
- If two patterns in the codebase contradict (e.g., two providers handling decimals or error mapping differently), do not average them. Pick one — typically the more recent or better tested — explain why, and flag the other for cleanup
During active implementation, rebase conflict resolution, or compile-fix loops, prefer targeted build/test commands and defer broad clippy/format runs until the change is ready to commit. Do not skip the required clippy/format checks silently before final handoff; run them then, or report the exact reason they are still pending.
Before finishing a task:
- Review for simplification — reduce duplication, extract helpers, consolidate modules, remove dead code
- Keep changes minimal — code must be concise and focused; reviewers cannot realistically review thousands of lines per PR, so only include what is necessary for the task
- Run tests:
just testorjust test <CRATE> - Run clippy:
cargo clippy -p <crate> -- -D warnings - Format:
just format
- Tests must verify intent, not just behavior. A test that still passes when the function returns a hardcoded constant is a tautology — fix the assertion or the function under test.
- Do not write tolerance-based assertions against live network values or values recomputed from separate RPC/API calls in integration tests. These tests are flaky and low-signal.
- For integration tests, assert stable invariants only. For exact numeric behavior, cover the pure calculation in unit tests with deterministic inputs.
- Write one test function with many assertions instead of many separate single-assertion test functions. Group related cases into a single
test_<function_name>test.
- Put reusable mocks in a crate
testkitfile and attach them to the type withimpl Type { pub fn mock() -> Self }. - Use
mock()for the default case; usemock_with_*or a clearly named variant only when needed. - Keep mocks small, valid, and fixed. If a fixture is only used once, an inline literal is fine.
Mock example:
impl Asset {
pub fn mock() -> Self {
Asset::from_chain(Chain::Ethereum)
}
}Examples: