name: finstack-consistency-reviewer description: Reviews finstack-quant code for cross-module consistency: naming conventions, pattern drift, Rust/Python/WASM naming triplets, builder/error/module conventions, and convention inventory updates. Use when the user asks to make patterns consistent, unify naming, check conventions, or find pattern drift. Prefer finstack-simplify for dedupe/API-surface consolidation and finstack-refactor for implementation changes.
Consistency Reviewer
Reviews code for consistency across the codebase. Unlike other reviewers that focus on correctness, performance, or simplicity, this skill ensures similar things are done the same way everywhere.
Quick Start
When asked to review for consistency, produce output in this format:
## Consistency Review: [scope]
### Summary
[1-2 sentence overview of findings]
### Findings
#### [SEVERITY] [Category]: [Title]
**Where:** file1.rs:L10, file2.rs:L20
**Pattern A:** `description of one approach`
**Pattern B:** `description of divergent approach`
**Recommendation:** [which pattern to standardize on and why]
Severity Levels
| Level | Meaning |
|---|---|
| Blocker | Public API inconsistency that confuses users or breaks expectations |
| Major | Internal pattern divergence that increases maintenance burden |
| Minor | Cosmetic inconsistency or missed std-lib opportunity |
| Nit | Style preference, acceptable if intentional |
Review Checklist
Work through each category. For every check, compare at least 3 instances across different modules before flagging.
1. Naming Conventions
Rust:
- Functions:
snake_caseconsistently (no camelCase leaks) - Types/Structs/Enums:
PascalCaseconsistently - Constants:
SCREAMING_SNAKE_CASEfor allpub const, including private constants within the same scope - Builder methods: either all use
set_prefix or none do (checkset_interp()vsbase_date()patterns) - Error constructors: consistent pattern (
Type::new()vsType::variant_name()vs helper methods) - Trait names: consistent verb/noun form (e.g.,
DiscountingvsDiscountvsDiscountable) - Module names: consistent singular vs plural (
instrumentvsinstruments)
Python bindings:
- Wrapper types:
Py{Type}prefix consistently (noPython{Type}or bare names) - Method names:
snake_casematching Rust names where possible -
__repr__/__str__: consistent format across all#[pyclass]types
WASM bindings:
- Wrapper types:
Js{Type}prefix consistently -
js_name:camelCasefor functions,PascalCasefor types - Constructor naming: consistent
new()vscreate()vsfrom_*()patterns
Cross-binding:
- Same concept uses same name in Rust, Python, and WASM (modulo casing rules)
- Feature parity: if Rust has it, bindings should expose it (or document why not)
2. Pattern Consistency
Error handling:
- All crate error enums use
#[non_exhaustive] - All crates define
pub type Result<T> = std::result::Result<T, Error> - Error location pattern: consistent
error.rsvserror/mod.rsacross crates - Error variant naming: consistent
{Source}Errorvs{Description}style -
#[from]and#[error(transparent)]used consistently for wrapped errors
Builders:
- Entry point:
Type::builder(id)consistently (vsTypeBuilder::new()vsTypeBuilder::default()) - Terminal method:
.build()consistently (vs.finish()vs.create()) - Required vs optional fields: same validation approach across all builders
- Return types:
Result<T>vsTconsistent for similar builder complexity
Trait implementations:
-
Send + Syncbounds: applied consistently to all public traits of the same kind -
Clone + Debug: applied consistently to all public types -
DisplayvsDebug: consistent approach for user-facing vs developer-facing output -
Default: implemented where appropriate, consistently across similar types -
From/Intoconversions: bidirectional where expected, consistent direction
Module structure:
-
mod.rsvs file-per-module: consistent threshold for when to split - Re-export patterns: consistent use of
prelude.rsvs rootpub use -
pub(crate)vspub: consistent boundary between internal and public APIs - Test organization:
#[cfg(test)] mod testsin same file vs separatetests/directory
3. Missed Rust Std-Lib & Language Features
-
Iteratormethods: manual loops where.map(),.filter(),.fold(),.collect()suffice -
Optioncombinators:if let Some(x) = opt { ... }where.map(),.and_then(),.unwrap_or()is cleaner -
Resultcombinators: match blocks where?,.map_err(),.context()is cleaner -
From/Into: manual conversion functions whereimpl From<A> for Benables.into() -
Display: customto_string()methods whereimpl Displayis standard -
Default: manualnew()with no args whereDefaultis idiomatic -
AsRef/Borrow: accepting&Stringwhere&str/impl AsRef<str>is idiomatic -
Cow<str>: unnecessaryStringallocation whereCowavoids cloning -
std::mem::take/replace: verbose swap patterns wheretake()is cleaner -
#[must_use]: missing on functions that return values that should not be ignored -
impl Into<T>parameters: accepting concrete types whereimpl Into<T>is more ergonomic - Slice patterns: index-based access where slice destructuring is clearer
-
matches!macro: verbose match blocks that returntrue/false -
let-else: match/if-let with early return wherelet ... else { return }is cleaner
4. Duplicate & Near-Duplicate Functionality
- Utility functions: same logic implemented in multiple modules (date helpers, math utils)
- Type aliases: same type aliased differently in different modules
- Validation logic: same checks repeated instead of shared validators
- Configuration parsing: same config patterns re-implemented per module
- Test helpers: same setup/assertion patterns duplicated across test modules
- Conversion functions: same
A -> Bconversion in multiple places
5. API Surface Consistency
- Similar instruments expose similar method sets (e.g., all bonds have
coupon_rate()) - Similar pricers accept similar parameters and return similar result types
- Curve types expose consistent query interfaces (
.discount(),.forward(), etc.) - All public types implement the same baseline traits (
Clone,Debug,Serialize,Deserialize) - Error messages follow consistent formatting patterns
6. Documentation Consistency
- All
pubitems have doc comments (or none do within a module -- pick one) - Doc comment style:
///vs//!used consistently - Examples in docs: consistent format (
# Examplessection with fenced Rust code blocks) - Module-level docs: consistent presence and format across similar modules
- Math notation: consistent use of LaTeX/Unicode across doc comments
Review Process
- Scope: Identify the modules/crates under review
- Inventory: List all instances of each pattern category within scope
- Compare: For each category, identify the dominant pattern and any deviations
- Classify: Is the deviation intentional (documented) or accidental (drift)?
- Recommend: For each inconsistency, recommend which pattern to standardize on
Decision Framework
When two patterns conflict, prefer:
- Majority wins: The pattern used in more places (lower migration cost)
- Std-lib idiomatic: The pattern closer to Rust conventions
- Public-facing: The pattern used in public APIs (harder to change)
- Newer code: The pattern in recently-written code (likely reflects current intent)
Output Template
## Consistency Review: [scope]
### Summary
Found X findings (Y blocker, Z major, W minor, V nit) across N files.
### Findings
#### [BLOCKER] Naming: Builder method prefix inconsistency
**Where:** discount_curve.rs:L1012 (`set_interp()`), hazard_curve.rs:L45 (`knots()`)
**Pattern A:** `set_` prefix on setter methods (3 occurrences)
**Pattern B:** bare name for setter methods (12 occurrences)
**Recommendation:** Standardize on bare names (majority pattern, more ergonomic)
#### [MAJOR] Pattern: Error module location
**Where:** core/src/error/mod.rs, portfolio/src/error.rs, scenarios/src/error.rs
**Pattern A:** `error/mod.rs` with submodules (1 crate)
**Pattern B:** `error.rs` flat file (3 crates)
**Recommendation:** Use `error.rs` for crates with <5 error variants, `error/mod.rs` for larger
### Convention Inventory
[Optional: table of all patterns found and their locations for reference]
Additional Resources
- For codebase-specific conventions, see conventions.md