ADR 0001 โ Canonical contract, versioning, and cross-language conformance¶
Status: accepted ยท Date: 2026-10-02
Context¶
Policy semantics must exist once (Rust), while a Go Kubernetes adapter consumes the same contract. The project needs a single wire contract, a way to detect divergence between Rust and Go, and a stable policy digest.
Decision¶
- Canonical types live in Rust (
crates/contracts). The versioned JSON Schemas underschemas/v1alpha1are generated from those types viaschemars. A test (committed_schemas_match_types) fails if the committed schemas drift from the code โ this is the CI divergence detector. - Schema version is
v1alpha1, carried inHardwareSnapshotandPlacementDecision. Breaking changes bump it; additive, backward-compatible fields do not. - Shared conformance fixtures (
fixtures/requests/*.json,fixtures/decisions/*.json) are generated by the Rust evaluator. Rust asserts it reproduces every decision; Go decodes the same vectors with unknown-field rejection and round-trips them. Semantic drift fails a test in either language. - Policy digest is
sha256:<hex>over a canonical JSON of the normalized policy (recursively sorted keys), not raw YAML bytes. - Go pins a core revision via the lab
versions.yaml; it never copies untracked schema fragments. Local dev uses sibling checkout paths.
Consequences¶
- Adding a predicate requires: update Rust types + reason code + evaluator, regenerate schemas + fixtures, extend Go structs, re-run both test suites.
- No separate hand-maintained schema can rot independently of the code.
- Strict parsing (
deny_unknown_fields+ duplicate-key rejection) is part of the contract, so unknown security fields and duplicate keys are validation errors.