- Rust workspace with stemedb-core crate - Full .claude/ configuration (agents, skills, commands, guides) - ai-lookup/ for token-efficient fact storage - Quality gates: clippy, fmt, jscpd duplication detection - Pre-commit hook with 5-phase quality checks - CLAUDE.md router and CODING_GUIDELINES.md standards Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
3.5 KiB
3.5 KiB
Rust Coding Guidelines
Core Stack
| Component | Required | Forbidden |
|---|---|---|
| Error handling | thiserror (lib), anyhow (bin) |
.unwrap(), .expect(), panic! |
| Serialization | rkyv (zero-copy) |
serde_json for hot paths |
| Hashing | blake3 |
sha256, md5 |
| KV Store | sled (MVP), trait-abstracted |
Direct rocksdb coupling |
| Async | tokio (if needed) |
async-std |
| Logging | tracing |
log, println! |
| Testing | proptest, rstest |
Mocking core types |
Error Handling
// Library errors: use thiserror
#[derive(Debug, thiserror::Error)]
pub enum StemeError {
#[error("assertion not found: {0}")]
NotFound(Hash),
#[error("invalid signature for agent {agent}")]
InvalidSignature { agent: AgentId },
#[error("storage error: {0}")]
Storage(#[from] sled::Error),
}
// Always add context
fn load_assertion(hash: &Hash) -> Result<Assertion, StemeError> {
let bytes = self.store
.get(hash)
.context("failed to read from store")?
.ok_or(StemeError::NotFound(*hash))?;
// ...
}
Type Safety
Use newtypes for domain IDs:
// Good: Strong types
pub struct EntityId(pub String);
pub struct RelationId(pub String);
pub struct Hash(pub [u8; 32]);
pub struct AgentId(pub [u8; 32]);
// Bad: Stringly-typed
fn query(subject: String, predicate: String) -> ...
Formatting
- Use underscores in numbers:
1_000_000not1000000 - Use inline interpolation:
println!("{hash:?}")notprintln!("{:?}", hash) - Max line length: 100 characters
- Run
cargo fmtbefore commit
Testing
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
// Property test for critical invariants
proptest! {
#[test]
fn assertion_round_trips(bytes in prop::collection::vec(any::<u8>(), 0..1000)) {
let assertion = /* create from bytes */;
let serialized = rkyv::to_bytes(&assertion)?;
let deserialized = rkyv::from_bytes(&serialized)?;
assert_eq!(assertion, deserialized);
}
}
// Descriptive test names
#[test]
fn test_lens_consensus_returns_highest_vote_count() {
// Arrange
let assertions = vec![/* ... */];
// Act
let result = ConsensusLens.resolve(&assertions, &ctx);
// Assert
assert_eq!(result.value, expected);
}
}
Module Structure
crates/stemedb-core/
src/
lib.rs # Public API, re-exports
assertion.rs # Assertion struct
types.rs # EntityId, RelationId, Hash, etc.
store.rs # Storage trait
error.rs # StemeError
tests/
integration.rs # Cross-module tests
Documentation
/// Resolves conflicting assertions using weighted voting.
///
/// # Arguments
/// * `candidates` - All assertions matching the query
/// * `context` - Query context including agent reputations
///
/// # Returns
/// The assertion with highest weighted support, or None if empty.
///
/// # Example
/// ```
/// let lens = ConsensusLens::default();
/// let result = lens.resolve(&assertions, &ctx);
/// ```
pub fn resolve(&self, candidates: &[Assertion], context: &QueryContext) -> Option<Assertion> {
// ...
}
Clippy
Must pass with zero warnings:
cargo clippy --workspace -- -D warnings
Common fixes:
needless_return: Remove explicitreturnwhen implicit worksredundant_clone: Check if clone is actually neededmissing_docs: Add doc comments to public items