# kyyn-core
Core vocabulary for [kyyn](https://github.com/drshade/kyyn): the registry
(kinds, fields, roles, affordances), the universal link grammar, the
serializable query AST and typed builder, the source-plugin contract, and the
validation protocol a KB's schema crate speaks.
A KB's `schema/` crate depends on this; the engine and the schema meet only
through these types.
## Schema authoring
Rust remains the canonical schema language. Derive `KyynKind` on stored
record structs and `KyynType` on nested structs or enums; the derives generate
the existing `Registry` vocabulary rather than a second schema format:
```rust
#[derive(serde::Serialize, serde::Deserialize, kyyn_core::KyynKind)]
#[kyyn(kind = "todo", storage = "facts/todos/{id}.ron")]
struct Todo {
id: String,
#[kyyn(role = "title")]
title: String,
#[kyyn(link(allowed = ["todo"]))]
blocked_by: Option<kyyn_core::link::Link>,
#[kyyn(markdown)]
notes: String,
}
```
`kyyn_schema!` assembles an explicit ordered kind list with the KB's roles,
queries, and optional custom-validation hook. Generic registry validation
always runs before custom findings are appended. Meaning Rust cannot infer —
Markdown rendering, roles, link policy and storage — must be explicit; an
ambiguous field is a compile error. Implement `KyynType` or `KyynKind`
manually for a representation outside the deliberately small derive surface.
Kind storage remains subject to Registry's existing ownership boundary: it
must be a relative path under `facts/` and may not contain `..`. `KyynKind`
reports that invariant at compile time; it does not introduce a macro-only
layout rule. For semantic annotations, do not shadow the standard `String`,
`Option`, `Vec`, or Kyyn `Link` names with unrelated types: early annotation
diagnostics identify those spellings before Rust resolves their definitions.
```rust,ignore
kyyn_core::kyyn_schema! {
kinds: [Charter, Todo, SelfContext],
roles: roles(),
queries: queries(),
custom_validate: validate::custom,
}
```
The optional custom hook has the same inputs as the protocol validator and
returns only its additional findings:
```rust,ignore
fn custom(
entries: &[(String, String)],
systems: &[&str],
) -> Vec<kyyn_core::violation::Violation> {
// Cross-record/domain checks; generic Registry validation already ran.
Vec::new()
}
```