# inline-validation-handlers ## AUDIT (2026-02-01) **Initial Assessment**: "No structured validation library" flagged as MEDIUM priority debt. **Investigation**: Thorough audit of all 12 handler files in `crates/stemedb-api/src/handlers/`. **Finding**: False positive. The codebase already has well-structured validation: ### Existing Validation Infrastructure 1. **Centralized hex module** (`crates/stemedb-api/src/hex.rs`): - `decode_hash_32()` - 32-byte hash validation - `decode_hash_8()` - 8-byte hash validation - `decode_agent_id()` - Ed25519 public key validation - `decode_signature()` - 64-byte signature validation - All functions validate length BEFORE decoding, with clear error messages 2. **Handler-specific dto_to_* functions**: - `dto_to_assertion()` in assert.rs - `dto_to_vote()` in vote.rs - `dto_to_epoch()` in epoch.rs - Each encapsulates conversion + domain-specific validation 3. **Consistent patterns across all handlers**: - Bounds checks: `if req.confidence < 0.0 || req.confidence > 1.0` - Empty checks: `if req.reason.trim().is_empty()` - Relationship validation: `if supersedes.is_some() && supersession_type.is_none()` ### Usage Count | Handler | Uses hex module | Domain validation | |---------|-----------------|-------------------| | assert.rs | ✅ 5 calls | ✅ confidence, signatures | | vote.rs | ✅ 3 calls | ✅ weight | | epoch.rs | ✅ 1 call | ✅ name, supersession | | supersede.rs | ✅ 4 calls | ✅ reason | | trace.rs | ✅ 1 call | ✅ timestamps | | query.rs | ✅ 3 calls | ✅ epoch | | audit.rs | ✅ 2 calls | - | | meter.rs | ✅ 1 call | - | | layered.rs | - (read-only) | - | ## RESOLUTION **Status**: CLOSED - No debt found **Reason**: The inline validation is intentional and appropriate: - Domain-specific rules are co-located with conversion logic - Shared validation (hex decoding) is already centralized - Adding a validation library would add complexity without benefit - Error messages are consistent via `ApiError::InvalidRequest` **No fixes applied.**