# 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.**
