Submit an issue View all issues Source
MIR-1239

schemagen silently drops required / choices / enum constraints (and has no CEL on-ramp)

Open public
phinze phinze Opened Jun 16, 2026 Updated Aug 31, 2026

While fact-checking the "Datoms All the Way Down" post against pkg/entity, we confirmed a gap worth tracking: the declarative YAML → schemagen path quietly drops several constraints it looks like it emits, so writes that should be rejected aren't.

The validation engine itself is fine. A db/attr.pred ref to a predicate entity carrying a db/program CEL string gets compiled and evaluated on every write (validation.go:318-353), and the shipped db/pred.ip / db/pred.cidr predicates prove it works end to end. The on-ramp is where things fall through:

  • required and choices look enforced but aren't. schemagen emits .Required(...) / .Choices(...) (generator.go:391-414, 729-761), but SchemaBuilder never reads ab.required, ab.choises, or ab.extra when it builds the descriptor (schema/schema.go:194-232). Silently dropped.
  • AdditionalAttrs is dropped too, which means Enum's EnumValues (set via AdditionalAttrs, schema.go:270-276) never lands on the descriptor.
  • type: enum generates as sb.Ref(...).Choices(...) instead of sb.Enum(...) (generator.go:763-764), so it registers as db/type.ref with no enum values, and the TypeEnum membership check (validation.go:274-280) never runs. Choices survive only as Go constants.
  • No YAML key for a CEL predicate exists, so the data-driven validation that does work can only be wired by hand through the entity API.

The dangerous part is the silence: a YAML schema reads as if required/choices are enforced, with no error or warning when they're not.

Directions for whoever picks this up: wire the builder to consume required/choices/extra; add a YAML key that emits db/attr.pred + db/program; and/or at minimum make schemagen loudly refuse constraints it can't enforce yet.

Found at commit 7a42b90b.