.claude/skills/interface-design-review/SKILL.md
Use when designing new public APIs, adding trait methods, or refactoring type signatures to ensure they are easy to use correctly and hard to use incorrectly. Reviews Rust interfaces for misuse resistance.
npx skillsauth add ahrav/gossip-rs interface-design-reviewInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Review public APIs, traits, structs, and module boundaries against the guiding principle: "Make interfaces easy to use correctly and hard to use incorrectly."
The correct path should be the path of least resistance. Incorrect usage should be caught at compile time when possible, at construction time when not, and at runtime only as a last resort.
pub fn, pub struct, pub trait)Prefer enforcement higher in this list. Each level down is weaker:
| Level | Mechanism | Example | Strength |
|-------|-----------|---------|----------|
| 1. Unrepresentable | Type system makes the wrong state impossible | Newtype, typestate, NonZeroU32 | Strongest |
| 2. Unconstructable | Invalid values can't be built | Private fields + validated new() | Strong |
| 3. Compile error | Misuse fails to compile | Ownership, lifetimes, trait bounds | Strong |
| 4. Visible at call site | Misuse is obvious when reading code | Enums over bools, named types over primitives | Moderate |
| 5. Runtime rejection | Validated at runtime with clear error | TryFrom, Result-returning methods | Weak |
| 6. Documentation | "Don't do this" in a doc comment | /// # Panics | Weakest |
u32/u64/usize values
that carry distinct meaning (file IDs, rule IDs, buffer indices) wrapped in
newtypes to prevent silent mixing?bool parameters? Replace
with a two-variant enum that documents intent at the call site.
// BAD: what does `true` mean here?
scan(data, true, false)
// GOOD: intent is self-documenting
scan(data, Encoding::Utf8, CaseSensitivity::Insensitive)
NonZero* for values that must not be zero — Capacity, counts, sizes.#[non_exhaustive] on enums that may grow to force _ => arms.// BAD: validated but raw — nothing stops later code from skipping validation
fn process(rule_yaml: &str) { validate(rule_yaml)?; /* use rule_yaml */ }
// GOOD: parsing produces a type that is valid by construction
fn process(rule_yaml: &str) -> Result<Rule> { Rule::parse(rule_yaml) }
TryFrom / TryInto — For conversions that can fail, is the
fallible path the only path?pub on fields with invariants — If start <= end must hold,
neither field should be pub.// Compile error if you forget `source`:
ScanConfig::builder()
.source(path) // required — returns SourceSet state
.max_depth(10) // optional
.build() // only available after required fields set
&str not &String,
impl AsRef<Path> not &PathBuf, impl Into<X> for ergonomic construction.Box<dyn Error> when a
concrete error enum would let callers handle cases. Don't return Vec<u8>
when a Digest newtype carries meaning.Default trait — If most callers want the same
config, implement Default so the common case is one line.self methods that return Self
guide callers through a fluent API.String/&str when an enum or newtype would be type-safe?copy(src, dst) or
range(start, end) match what a caller would guess?get_ vs fetch_ vs load_)?#[must_use] on results — Can the caller silently ignore a return
value that indicates failure or carries important data?
#[must_use = "dropping the guard releases the lock immediately"]
pub fn acquire(&self) -> LockGuard<'_> { ... }
fn set_flag(&mut self, flag: u32),
use fn enable_unicode(&mut self).&T for
reads and require owned T or &mut T for writes?Cell, RefCell,
Mutex) is necessary, is it encapsulated rather than leaked through the API?Result rather than
unwrap/expect. Reserve panics for genuine invariant violations in internal
code.| Anti-Pattern | Why It's Bad | Fix |
|---|---|---|
| fn foo(verbose: bool, recursive: bool) | Callers write foo(true, false) — meaning is invisible | Use enums: Verbosity::Quiet, Traversal::Flat |
| pub struct Range { pub start: u32, pub end: u32 } | Nothing enforces start <= end | Private fields + Range::new(start, end) -> Result<Range> |
| fn process(id: u64, parent_id: u64) | Easy to swap arguments silently | fn process(id: RuleId, parent: ParentId) |
| fn configure(opts: HashMap<String, String>) | Unbounded input, typos compile fine | Typed config struct or builder |
| fn init() -> *mut State | Caller must remember to free, can use-after-free | Return Box<State> or Arc<State> |
| Returning i32 error codes | Caller can ignore, misinterpret, or mix with valid values | Return Result<T, E> with typed error |
| fn send(data: &[u8], compress: bool, encrypt: bool, sign: bool) | 3 bools = 8 combinations, many invalid | Options struct or builder with valid combinations |
| Silent default on invalid input | Caller doesn't know their input was wrong | Return Err or use a type that can't be invalid |
| Parsing config twice into similar structs | Layers silently drift when defaults change in only one place | Parse once, validate once, derive downstream views from the validated config |
| Treating a single positional token as multiple possible modes | Callers cannot predict how edge cases are classified | Use explicit syntax or add parser tests for each ambiguous shape |
This codebase already uses several "easy to use correctly" patterns. New code should follow them:
NONE_U32 = u32::MAX — centralizes the
sentinel so callers don't invent their own.<const G: usize>
prevents runtime branching on a configuration value.#[cfg(feature = "test-support")] ensures test
infrastructure (e.g., Arbitrary impls, sim harness) can't accidentally leak
into production builds.debug_assert! for internal invariants: Zero-cost in release, catches
contract violations during development.When reviewing, check that new APIs are consistent with these existing patterns.
## Interface Design Review: [module/type/function]
### Enforcement Level Assessment
| Aspect | Current Level | Target Level | Gap |
|--------|--------------|--------------|-----|
| ID parameters | 6 (docs say "pass rule ID") | 1 (newtype `RuleId`) | 5 levels |
| Construction validity | 5 (runtime check in `new()`) | 2 (`new()` → private fields) | 3 levels |
| Flag parameters | 6 (doc: "`true` = verbose") | 4 (enum `Verbosity`) | 2 levels |
### Findings
| Priority | Issue | Location | Enforcement Gap |
|----------|-------|----------|-----------------|
| HIGH | Raw `u64` used for rule ID and parent ID — easy to swap | `fn register(id: u64, parent: u64)` | Unrepresentable → Newtype |
| MEDIUM | `bool` parameter for mode selection | `fn scan(data: &[u8], lenient: bool)` | Visible → Enum |
| LOW | Missing `#[must_use]` on `Result`-returning fn | `fn validate()` | Already level 5, add attribute |
### Recommendations
1. **[Issue]**: [Fix with code showing before/after]
```rust
// Before: callers can silently swap IDs
pub fn register(id: u64, parent: u64) { ... }
// After: compiler rejects swapped arguments
pub fn register(id: RuleId, parent: ParentId) { ... }
## Related Skills
- `/security-reviewer` - Complements this: security reviews focus on memory
safety; interface reviews focus on API misuse prevention
- `/test-strategy` - Property-based tests are powerful for validating that an
interface's invariants hold across all inputs
- `/doc-rigor` - If an interface passes this review, its docs should describe
*why* the design prevents misuse, not just *how* to use it
development
Deep first-principles code explanation that builds real understanding through phased walkthroughs with diagrams. Covers algorithms, data structures, memory layout, concurrency patterns, and performance tricks — especially for systems code in Rust. Use whenever the user asks to explain, walk through, break down, deep dive into, or understand code. Trigger on "how does this work", "what's happening here", "teach me about this", "why is it done this way", or when the user references a file with @ and wants to understand it. Proactively use when examining code involving lock-free algorithms, atomics/CAS, memory ordering,
development
Use when creating implementation-ready beads tasks that need testing strategy, optimal implementation approach, and documentation requirements baked in — composes /create-task with parallel enrichment agents that analyze the codebase and produce concrete test specifications, algorithm/data-structure guidance, and doc quality standards so implementing agents don't need to re-research
development
--- name: autoresearch description: Autonomous Goal-directed Iteration. Apply Karpathy's autoresearch principles to ANY task. Loops autonomously — modify, verify, keep/discard, repeat. Supports bounded iteration via Iterations: N inline config. version: 1.9.11 --- # Claude Autoresearch — Autonomous Goal-directed Iteration Inspired by [Karpathy's autoresearch](https://github.com/karpathy/autoresearch). Applies constraint-driven autonomous iteration to ANY work — not just ML research. **Core id
development
Use when implementing a new feature and assessing coverage gaps, during periodic test hygiene, when test suites feel bloated, or before merging code that changes coordination or hot paths. Two-phase assess-then-improve testing pipeline.