stemedb/CODING_GUIDELINES.md
jordan a776744889 Initial project setup with Claude Code monorepo structure
- 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>
2026-01-31 10:56:26 -07:00

145 lines
3.5 KiB
Markdown

# 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
```rust
// 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:
```rust
// 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_000` not `1000000`
- Use inline interpolation: `println!("{hash:?}")` not `println!("{:?}", hash)`
- Max line length: 100 characters
- Run `cargo fmt` before commit
## Testing
```rust
#[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
```rust
/// 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:
```bash
cargo clippy --workspace -- -D warnings
```
Common fixes:
- `needless_return`: Remove explicit `return` when implicit works
- `redundant_clone`: Check if clone is actually needed
- `missing_docs`: Add doc comments to public items