kitt_score 0.1.0

Decision engine at the core of Project KITT — in-memory stateful matching with pluggable scoring backends.
Documentation
# Events

This note covers `src/event/*`.

## Design intent

The crate models three event shapes directly instead of forcing all callers through one runtime-discriminated payload. The optional [[#Event]] enum exists only for adapter convenience.

## `AttrSet`

**Defined in:** `src/event/attr_set.rs`

A borrowed event-time bundle of attribute values.

### Internal shape
`SmallVec<[(AttrId, Value<'a>); 8]>`

### Why this shape exists
Most events are small. Inline storage avoids a heap allocation in the common case.

### Public API
- `new()`
- `push(id, value)`
- `len()`
- `is_empty()`
- `iter()`
- `FromIterator`

### Relationships
- used by [[#StateUpdate]] and [[#Trigger]],
- contains [[Modules/Identifiers and Time#AttrId]] plus [[Modules/Schema#Value]].

## `Event`

**Defined in:** `src/event/event_enum.rs`

A convenience enum for callers that deserialize one wire payload and then dispatch.

### Variants
- `Update(StateUpdate<'a>)`
- `Register(ActionIngest<'a, T>)`
- `Trigger(Trigger<'a>)`

### Important nuance
This enum is **not** the engine's hot-path abstraction. The performance-sensitive API remains the three explicit ingest methods.

## `KindRef`

**Defined in:** `src/event/kind_ref.rs`

A caller-facing kind handle.

### Variants
- `Id(KindId)`
- `Name(&str)`

### Semantics
- `Id` is the zero-lookup fast path.
- `Name` trades a small amount of lookup work for ergonomics.

## `ActionIngest`

**Defined in:** `src/event/action_ingest.rs`

The registration-time input for one action.

### Fields
- `location: LocId`
- `action_id: ActionId`
- `start: UnixTime`
- `end: UnixTime`
- `priority: u8`
- `kind: KindRef<'a>`
- `scorer: ScorerSpec<'a>`
- `payload: T`
- `post: Option<Arc<dyn Fn(&T, &LocationView) -> T + Send + Sync>>`

### Semantic meaning
This is where a business-level candidate becomes an engine-level action.

### Important implementation note
The current engine compiles the scorer and stores the payload, but it does **not** preserve `kind` in [[Modules/Location State#ActionEntry]]. So `kind` is part of the registration API surface, but not part of current action eligibility logic.

## `StateUpdate`

**Defined in:** `src/event/state_update.rs`

A mutable event that writes values into a location's state.

### Fields
- `location`
- `kind`
- `attrs`

### Semantics
A successful update overwrites the prior values for the addressed slots, marks the kind as present, and increments the location version.

### Current behavior detail
If `apply_update` fails, `Engine::ingest_update` currently collapses that failure into `IngestErr::UnknownKind`, even though the real cause could also be an unknown attribute or a type mismatch.

## `Trigger`

**Defined in:** `src/event/trigger.rs`

A decision request event.

### Fields
- `location`
- `kind`
- `attrs`

### Semantic intent
The trigger is supposed to represent a scoring opportunity, optionally carrying per-trigger context.

### Current implementation reality
- `kind` is currently not used to filter actions.
- `attrs` are currently not visible to scorers because [[Modules/Location State#LocationView]] has no trigger-attribute channel.

So the current trigger path is driven entirely by persisted location state plus action registration state.

## Summary

The event layer is clean and expressive, but it is slightly ahead of the currently enforced runtime semantics. That is why the distinction between **API shape** and **current behavior** matters when reading the codebase.