Skip to main content

Module runtime_validator

Module runtime_validator 

Source
Expand description

Runtime CRUD validators consumed by the unified crate::Engine — the single mutation engine, whatever storage backend (mem-repo git branch, plain folder, archive) sits behind a mount.

Distinct concern from crate::validator, which validates sealed archive bytes at the registry / read-mem ingress boundary. This module sits inside the mutation engine and gates per-mutation payloads (section keys, metadata keys, enum values) against the pinned schema. The wire-format error codes (UNKNOWN_SECTION, UNKNOWN_METADATA, INVALID_ENUM_VALUE, MISSING_REQUIRED_SECTION) are stable regardless of workspace storage, so MCP callers always see the same envelope shape.

Returns a typed ValidationError (or a list of MissingRequiredSection for the warning surface) — the engine layer above wraps these into its error/Result type.

Structs§

MissingRequiredField
Tier-2 warning shape — the create path emits one entry per required metadata field that is not auto-filled by the schema (no default_value, no init_timestamp, no auto_timestamp) and was not supplied by the caller. Same payload the MCP layer surfaces as MISSING_REQUIRED_FIELD warnings — mirrors the REQUIRED_FIELD_UNSET error envelope so a single decoder handles both surfaces.
MissingRequiredSection
Tier-2 warning shape — the create / update path emits one entry per required section that is missing or empty. Same payload the MCP layer surfaces as MISSING_REQUIRED_SECTION warnings. Type-level write_rules no longer ride per warning — they ship once at the mutation-response top level on type_guidance keyed by entity_type (F9).
RelationshipHint
Compact relationship-vocabulary entry — name plus optional when_to_use prose. Surfaces inside ValidationError::InvalidRelationshipType recovery payloads so an agent reads the canonical vocabulary in the same response that rejected the call. Mirrors the public RelationshipHint shape in memstead-git-branch; the engine adapter there converts between the two with a 1:1 field copy.

Enums§

CrossMemRelCheck
Outcome of looking up a rel-type against a cross-mem entry in the source schema’s cross_mem_relationships: vocabulary. EdgeNotDeclared carries the recovery payload the engine layer wraps into crate::EngineError::CrossMemEdgeNotDeclared; the other variants reuse the existing ValidationError shapes so agents reading the wire shape decode INVALID_REL_TYPE / INVALID_REL_SHAPE identically in both intra- and cross-mem flows.
RelationshipCheck
Outcome of running a relationship name against a schema. The engine adapter above decides whether to ride the warning out on the response (open mode) or convert the error into its own type (strict mode).
ValidationError
A typed CRUD-time validation failure. Mirrors the wire-format error codes the MCP layer surfaces; the engine adapters convert each variant into their own error type.

Constants§

READ_ONLY_METADATA_KEYS
Reserved metadata keys — the entity’s identity/discriminator triple. No write path accepts them as caller-supplied metadata (create and update both refuse a set); letting them ride the schema’s declared fields would silently drift the entity-id contract. Unset is the one sanctioned exception: metadata_unset may name a reserved key to repair an entity that acquired a smuggled one before the write gates closed — removing a reserved key can only move the entity toward the invariant (the type discriminator is re-seeded by the engine, never left absent).

Functions§

is_date_shaped
Does s match the shape a Date-typed metadata value must have — YYYY-MM-DD or the ISO-8601 datetime form YYYY-MM-DDTHH:MM:SSZ?
missing_required_fields
Return one MissingRequiredField per required metadata field that the caller did not supply and the schema does not auto-fill. A field is “auto-filled” when it carries default_value, init_timestamp, or auto_timestamp — the engine writes a non-trivial value without caller input. Optional fields and supplied fields are skipped.
missing_required_sections
Return one MissingRequiredSection per required section that is absent or empty in sections. Empty (whitespace-only) bodies count as missing — same predicate as the health report uses.
parse_metadata_value
Parse a metadata value string into the appropriate MetadataValue type, consulting the schema for field-type information. Validates enum constraints when the field definition specifies enum_values.
validate_cross_mem_edge
Validate a cross-mem edge whose source and target mems pin schemas with different names against the source schema’s outbound cross_mem_relationships: vocabulary.
validate_rel_shape
Reject an edge whose (from_type, to_type) pair violates the schema’s declared source_types / target_types for this relationship. No-op when both constraint lists are empty (shape-free edges) or when the relationship name is unknown (callers run this only after validate_rel_type succeeds, so this branch is defensive). The target-type check is skipped when to_type is None — happens for auto-stubbed targets that have no type yet; once the stub is authored as a real entity, future edges land under the strict check.
validate_rel_type
Validate a relationship name against a mem schema’s vocabulary. Strict-mode schemas reject undeclared names with ValidationError::InvalidRelationshipType; open-mode schemas admit unknown names and return a warning string for the engine to surface.
validate_reserved_metadata_key
Reject a caller-supplied reserved identity/discriminator key (mem / id / type) as metadata — the create-path half of the reservation, deliberate and typed (ValidationError::ReadOnlyField) rather than the incidental UNKNOWN_METADATA_FIELD a reserved key would otherwise trip (no installable schema can declare one). Timestamp fields are NOT checked here: create’s posture for init_timestamp / auto_timestamp fields is stamp-and-proceed with an IGNORED_READONLY_FIELD warning, deliberately.
validate_section_content
Refuse section content that would round-trip through the compose pipeline as a section delimiter. The compose-then-reparse loop’s parser anchors on (?m)^## (.+)$, so a section body containing a ^## line gets split at that heading on the next read — content after the heading lands under a different section key (or a fabricated one). Deeper headings (### and below) are safe — the parser only matches level 2.
validate_section_keys
Validate that every section key in provided is either schema-declared for schema, or — if the schema has a catch-all section — admitted by it. Unknown keys return ValidationError::UnknownSection carrying the declared list plus a Levenshtein suggestion (or the catch-all key when no close match exists).
validate_unsettable_metadata_key
Gate for metadata_unset keys. Unlike the set path (validate_writable_metadata_key), the reserved identity/discriminator triple (mem / id / type) IS unsettable: removing one can only move an entity toward the invariant, and it is the sanctioned repair for entities that acquired a smuggled reserved key before the write gates closed (delete-and-recreate would destroy provenance and edges). Engine-stamped timestamp fields (init_timestamp / auto_timestamp) stay refused on unset — the engine owns their values and re-stamps them; unsetting one is caller confusion, not repair.
validate_updatable_section
Reject an memstead_update attempt to write a section that is either the virtual relationships surface (managed by memstead_relate) or not part of the type’s updatable_fields allowlist. When the allowlist is empty the section passes — types that opt out of the allowlist accept any declared section.
validate_writable_metadata_key
Reject any attempt to set a read-only metadata key. The single mutation engine (memstead-base) calls this from its update_entity path over the metadata map: the mem / id / type triple stays engine-authoritative, and the schema’s init_timestamp / auto_timestamp annotations are honoured on write — the engine owns those values on create (init_timestamp, set once) and on every update (auto_timestamp, re-stamped). Returns ValidationError::ReadOnlyField on rejection. The unset path has its own gate (validate_unsettable_metadata_key) because the reserved triple is unset-allowed there as the sanctioned repair.