khive-runtime 0.5.0

Composable Service API: entity/note CRUD, graph traversal, hybrid search, curation.
Documentation
# KG Validation Pipeline

`validation.rs` defines the pack-contributed KG validation API: the `ValidationRule` /
`ValidationContext` / `Violation` types a pack declares through `PackRuntime::validation_rules()`.
Two distinct validation surfaces exist under ADR-034, in different states of wiring — this
document keeps them separate, because only one of them runs today.

## ADR Links

- [ADR-034]../../../../docs/adr/ADR-034-kg-validation-pipelines.md — KG validation pipelines specification

## 1. Shipped: the data-driven TOML RulePass

What `kkernel kg validate` actually executes today is the ADR-020 built-in structural checks
plus an optional TOML RulePass: when `.khive/kg/rules.toml` exists and `--no-rules` is not set,
each `[[rules]]` entry (`kind = "entity"` or `"edge"`, optional `condition`, optional
`require_field`, `message` with `{id}` substitution) runs after the structural pass. The TOML
file also accepts five opt-in named rule-class tables, evaluated after the generic `[[rules]]`
entries: `edge_endpoint_types` (endpoint contract against the live pack/base allowlist),
`edge_direction_conventions`, `dangling_refs`, `naming_conventions`, and `citation_date_lint`
(full syntax: `RulesFile` in `crates/kkernel/src/kg/validate.rs` and the kg-rules guide,
`crates/kkernel/docs/kg-rules.md`). Severity for these data-driven rules is configured per
rule in `rules.toml`:

- `Error``kkernel kg validate` exits with code 1.
- `Warning` — reported; no exit-code effect unless `--strict`.
- `Info` — informational; no exit-code effect.

This TOML pass does not go through the Rust `ValidationRule` API below — it is a separate,
data-driven runner.

## 2. Declared but deferred: the Rust pack-validator API

The types in `validation.rs` are shipped API surface, but **the CLI runner does not call them**
(ADR-034 "What changes and what does not"): `kkernel kg validate` never invokes
`PackRuntime::validation_rules()` against live corpus data, `VerbRegistry::all_validation_rules`
currently has no non-test caller, no runtime-populated `ValidationContext` is constructed, and a
declared rule cannot yet affect validation output, CLI exit status, or auto-fix behavior. Wiring
this API into the CLI runner is explicitly deferred by ADR-034.

### Rule Declaration

Pack authors declare an array of `ValidationRule` in their `PackRuntime::validation_rules()`
method. Rule IDs must follow the `<pack>/<rule-id>` namespace convention. Built-in rules
use no pack prefix (e.g. `"min-edge-density"`).

### Rule Shape

```rust
pub struct ValidationRule {
    pub id: RuleId,          // "<pack>/<rule-id>"
    pub severity: Severity,  // Info | Warning | Error
    pub description: &'static str,
    pub check: RuleFn,       // fn(&ValidationContext) -> Vec<Violation>
    pub fix: Option<FixFn>,  // None = unfixable
}
```

The `Severity` values carry the same intended exit-code semantics as §1, but for
pack-declared rules those semantics are design intent, not current behavior — nothing
executes these rules yet.

### GraphPatch

`GraphPatch` is a deferred stub (ADR-034 §auto-fix write path is deferred). The auto-fix
write path is not implemented; `fix: Some(...)` is reserved for future use.

### Invariants (design contract for the deferred runner)

- Rule IDs must be unique across all loaded packs.
- Pack-contributed rules must carry the pack namespace prefix.
- `CorpusCheck` receives a `GraphSnapshot`; it must not reach through to the storage layer.

### Failure Modes (apply once the runner is wired)

| Condition           | Behaviour                                                 |
| ------------------- | --------------------------------------------------------- |
| Duplicate rule ID   | Not enforced at boot in v0.2; first registered wins       |
| Fix function panics | Propagates as runtime panic; fix functions must not panic |
| Config key unknown  | Ignored; rules use their default severity                 |