openkind-core 0.1.0

Phase 0: Jev-compatible schema, validation, and JSON schemas for openkind.
Documentation
# openkind-core

> The canonical Jev wire contract, data structures, and validation rules for `openkind`.

`openkind-core` is the foundational crate of the `openkind` workspace. It defines the Jev wire types (`SystemRequest`, `SystemResponse`, `Question`, `Answer`, `State`) and the validation invariants of the [TypeSafe Jev API contract](https://docs.typesafe.ai/api), spoken over HTTP and gRPC.

It depends on no other workspace crate. The engine, API, server, CLI, client, runtime, backends, bench, and schema-generation crates all depend on it.

## Quickstart

```rust
use std::collections::HashMap;

use openkind_core::{
    validate_request, NoulQuestion, Question, State, SystemRequest, WireHashState,
};

let mut questions = HashMap::with_hasher(WireHashState::default());
questions.insert(
    "is_urgent".into(),
    Question::Noul(NoulQuestion {
        instructions: serde_json::json!("Does this message convey urgency?"),
        criteria: None,
    }),
);

let request = SystemRequest {
    state: State::Text("Server down in production!".into()),
    model: "mock".into(),
    questions,
};

assert!(validate_request(&request).is_ok());
```

Question maps use `WireHashState`, a foldhash-based hasher, so construct them with `HashMap::with_hasher(WireHashState::default())` rather than `HashMap::new()`.

## Wire types

- `SystemRequest`: the `/v1/systemone` request body, with `state`, `model`, and a map of question ID to `Question`. `API_VERSION` (`"jev-compatible-0.1"`) pins the wire version.
- `SystemResponse`: one `Answer` per question ID plus required token `Usage`.
- `Question`: a tagged enum of `NoulQuestion`, `ChoiceQuestion`, and `ScoreQuestion`. `instructions` accepts a string, object, or array, and Choice criteria values may be `null`.
- `Answer`: a tagged enum of `NoulAnswer`, `ChoiceAnswer`, and `ScoreAnswer`. `NoulAnswer` carries only `noul` and has no `confidence` field, per the spec.
- `State`: polymorphic input accepting plain text, a JSON object, or a JSON array.
- `ModelInfo` / `ModelsResponse`: wire structures for `GET /v1/models`.

Every wire float (`probabilities`, `score`, `noul`, `confidence`) is `f64`, so JSON round-trips exactly; an `f32` would turn `0.92` into `0.9200000166893005`.

## Validation

- `validate_request(&req)` rejects empty question maps, empty instructions, Choice questions without options, Score rubrics with fewer than two non-empty levels, and Noul criteria with empty `true`/`false` descriptions. It also caps requests at 10,000 questions and questions at 10,000 criteria options.
- `validate_response_for_request(&resp, &req)` and its retained `ResponseContract` check each answer against its request: answer type, selected Choice option, probability keys, sum-to-one distributions within tolerance, `confidence` and `noul` bounds in `[0.0, 1.0]`, and Score legend, index, and score consistency. `validate_response(&resp, &criteria)` remains for callers that only hold a criteria map.

## JSON schemas

The request, question, answer, and state types derive `schemars::JsonSchema`. The committed schemas under [`schemas/`](schemas/) are generated by the separate `openkind-gen-schemas` crate with `cargo run -p openkind-gen-schemas -- --write` and are never hand-edited.

## Testing

```bash
cargo test -p openkind-core
```

Module unit tests sit in `src/`, and [`tests/conformance.rs`](tests/conformance.rs) pins the wire format to examples and rules from the Jev spec.

## License

See the [MIT license](../../LICENSE). Cargo metadata declares `MIT OR Apache-2.0`.