ferro-projections 0.2.21

Service projection definitions for the Ferro framework
Documentation
# ferro-projections Crate Instructions

## Crate Purpose

Schema-only service definitions and the modality-agnostic `Renderer` trait. No runtime engines, no closures. Everything serializable and introspectable.

**Boundary rule:** ferro-projections owns the `Renderer` trait, `derive_intents()`, `ServiceDef`, and `TemplateRenderer` (generic JSON output). Concrete renderers for specific output formats (JSON-UI, WhatsApp, voice) live in their respective output crates, not here. Do not add rendering dependencies to this crate.

## Key Conventions

### Naming
- Types: `ServiceDef`, `FieldDef`, `FieldMeaning`, `StateMachine`, `ActionDef`
- Builders: method chaining with `mut self -> Self` (consuming builder)
- Guards/side effects: always strings, never closures

### Serialization
- All public types derive `Serialize, Deserialize, Debug, Clone`
- Use `#[serde(rename_all = "snake_case")]` on enums
- Custom(String) variants use `#[serde(untagged)]` where appropriate

### Module Structure
```
src/
  lib.rs          — re-exports, crate docs
  service.rs      — ServiceDef
  field.rs        — FieldDef, FieldMeaning, DataType, infer_meaning
  error.rs        — Error enum
  state.rs        — StateMachine, Transition (future)
  action.rs       — ActionDef, Precondition (future)
  relationship.rs — Relationship, Cardinality (future)
  intent.rs       — Intent enum (future)
  resolved.rs     — ResolvedField (future)
  graph.rs        — IntentGraph, IntentNode, IntentContext (future)
  renderer.rs     — Renderer trait, RenderOutput (future)
  renderers/      — Renderer implementations (future)
```

### Builder Pattern
```rust
ServiceDef::new("order")
    .display_name("Order")
    .field("total", DataType::Float, FieldMeaning::Money)
    .field("status", DataType::String, FieldMeaning::Status)
```

### Testing
- Every type: construction + serde round-trip
- Builders: chain validation
- State machines: unreachable state detection
- Intent graphs: edge correctness

### Anti-patterns
- No closures in definitions (breaks serialization)
- No runtime logic in ServiceDef (it's a schema)
- No trait objects for guards (use string references)
- No Default implementations that hide required fields