This skill complements CLAUDE.md and .claude/rules/rust-patterns.md. It must stay aligned with both and applies as an execution discipline layer on top of the project's Rust standards.
This skill adds execution rigor:
- ATDD/TDD workflow
- SRP, DRY, and OCP decision rules
- explicit dependency injection discipline
- comment quality standards
- structured implementation and review checks
If CLAUDE.md is stricter on any point, follow CLAUDE.md.
- implementing a new feature
- refactoring Rust code for maintainability
- reviewing architecture, module boundaries, or dependency flow
- tightening tests around user-visible behavior
- replacing hardcoded wiring with explicit dependency injection
Inspect the crate or workspace first. Read
Cargo.toml, localCLAUDE.md, crate layout, feature flags, tool configs, and existing tests.Define acceptance behavior first. Express the user-visible outcome before writing implementation details.
Add or update an acceptance-level test when the project has that layer. Otherwise, write the closest boundary-level integration test.
Add the next smallest failing test. Prefer a focused unit or module test for the next behavior increment.
Implement the minimum change that makes the test pass. Keep the diff tight. Do not rewrite unrelated code.
Refactor while green. Improve naming, cohesion, dependency flow, and readability without changing behavior.
Keep standards in sync. If the task materially changes project conventions, architecture, or workflow expectations, update
CLAUDE.mdor the relevant skill in the same change.Verify locally. Run formatting, linting, and tests appropriate to the affected crate or workspace.
- Keep project structure clean and predictable
- SRP: each module, type, and function should have one clear reason to change
- DRY: remove repeated validation, mapping, branching, and policy logic when the abstraction improves clarity
- OCP: extend behavior through composition, traits, enums, and additive configuration instead of invasive branching or copy-paste forks
- Prefer domain-oriented module boundaries over technical dumping grounds
- Keep domain logic separate from transport, persistence, configuration, and presentation concerns
- Prefer the smallest coherent abstraction that solves the real duplication or extension point
- Do not introduce trait-first abstractions without real consumer pressure
- Use constructor injection for long-lived collaborators
- Use function parameters for short-lived collaborators and pure logic inputs
- Pass dependencies through typed config, constructors, or arguments
- Do not instantiate external clients, repositories, clocks, or runtime collaborators inside core domain logic
- Do not hardcode dependency selection, URLs, ports, credentials, or feature switches
- Avoid globals and hidden singletons
Rust-specific guidance:
- Prefer concrete types internally
- Introduce traits at consumer boundaries or when multiple implementations are real
- Prefer generic parameters or references for local flexibility
- Use
Arconly when shared ownership is actually required
Keep the code easy to read and maintain.
- Write modern stable Rust and prefer Rust 2024 idioms in new code; key 2024 changes:
let-chains (if let Ok(x) = a && let Some(y) = b { … }),unsafeblocks required insideunsafe fn, stricterimpl Traitlifetime capture,genkeyword reserved - Keep functions short, explicit, and focused on one job
- Use consistent formatting, indentation, and whitespace; let
rustfmtdefine layout - Use whitespace to separate concepts, not decorate code
- Keep naming concrete and intention-revealing
- Prefer straightforward control flow over clever compression
- Separate logic clearly so domain rules, I/O, transport, persistence, and orchestration are easy to trace
- Avoid hardcoded values; move runtime values and environment behavior into typed config, constants, or inputs
- Prefer private fields on public structs unless direct field access is a deliberate part of the API
- Use standard traits on public types when they are semantically correct
- Use
#[non_exhaustive]for public enums and structs that are expected to grow - Avoid exposing unstable dependency types in public APIs when stable local types will do
- Do not implement
Dereffor wrapper types unless they are genuinely pointer-like
- ATDD first for user-visible changes: define the acceptance scenario before writing implementation
- TDD for the next increment: write the smallest failing unit test, implement, then refactor
- Use ATDD extensively for user-visible behavior, acceptance flows, and cross-boundary scenarios
- Every meaningful change should cover happy path, invalid input, edge cases, and failure paths
- Add concurrency-focused tests when ownership, async behavior, locking, or task orchestration can fail
- Keep tests deterministic; avoid sleep-based async tests when possible
- Keep public API doctests and examples accurate when behavior changes
Good comments explain:
- intent
- invariants
- ownership or concurrency rules
- safety conditions for
unsafe - non-obvious tradeoffs
Do not write comments that:
- restate the code
- narrate simple assignments
- explain obvious syntax
- leave vague TODOs without context
- Prefer compile-time guarantees over runtime interpretation whenever practical
- Make invalid states unrepresentable with enums, newtypes, constructors, and validated inputs
- Keep types specific and explicit; avoid stringly typed state and loosely shaped maps where a real type belongs
- Keep error handling clear, concise, and contextual
- Return specific error types from internal APIs rather than
String - Use
Resultwhen callers need failure information andOptiononly when absence is the whole story - Do not use casual
unwrap()orexpect()in production code
- Require an explicit shutdown path and error owner before approving any new background task
- Flag lock-across-await immediately; accept only with a documented reason
- Reject adding a second async runtime to a project that already has one
- Treat shared mutable state as a design smell; push back toward ownership transfer or channels
- Run
cargo fmt --all - Run
cargo clippy --all-targets --all-features -- -D warnings - Run
cargo nextest run(orcargo testif nextest is not installed) - If features or public docs changed, also run
cargo test --all-features,cargo test --no-default-features --features vt100-backend,cargo test --doc, andcargo doc --no-deps - Treat linting and static analysis as normal development tools, not release-only checks
- module and crate boundaries are coherent
- responsibilities are not mixed across domain, transport, persistence, and config
- structure is clean, predictable, and free of dumping-ground modules
- dependencies are injected explicitly
- no hardcoded runtime values or hidden collaborator construction remain
- functions are short and readable in one pass
- formatting, indentation, and whitespace follow project conventions and
rustfmt - comments explain intent or invariants instead of restating the code
- public API remains minimal, intentional, and stable
- types are specific, strict, and intentional, not stringly typed
- error handling is clear, concise, contextual, and free of casual
unwrap()/expect() - async and concurrency behavior follow the project's runtime and locking rules
- tests cover acceptance behavior and the next unit-level behavior
- formatting, clippy, and tests pass for the affected scope
- public API and docs stay aligned with the implementation
- giant functions mixing validation, orchestration, and persistence
- trait-per-struct abstraction without consumer need
- hardcoded configuration or collaborator construction
- comments that restate code
- hidden globals or singletons
- brittle mock-only tests when a fake or boundary test would be clearer
- transport or storage concerns embedded in core domain logic
- public APIs that leak unstable dependency details without need
- locks held across
.awaitor blocking work inside async tasks - production design distorted to satisfy a test double or framework
- large speculative refactors when a smaller coherent change would solve the task
- changes are small, test-backed, and easy to review
- dependency flow is explicit
- module responsibilities are cleaner after the change, not blurrier
- the implementation matches the Rust standards in
CLAUDE.mdand.claude/rules/ - the resulting code is easier to extend without rewriting stable behavior