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§
- Missing
Required Field - 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, noinit_timestamp, noauto_timestamp) and was not supplied by the caller. Same payload the MCP layer surfaces asMISSING_REQUIRED_FIELDwarnings — mirrors theREQUIRED_FIELD_UNSETerror envelope so a single decoder handles both surfaces. - Missing
Required Section - 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_SECTIONwarnings. Type-levelwrite_rulesno longer ride per warning — they ship once at the mutation-response top level ontype_guidancekeyed byentity_type(F9). - Relationship
Hint - Compact relationship-vocabulary entry —
nameplus optionalwhen_to_useprose. Surfaces insideValidationError::InvalidRelationshipTyperecovery payloads so an agent reads the canonical vocabulary in the same response that rejected the call. Mirrors the publicRelationshipHintshape inmemstead-git-branch; the engine adapter there converts between the two with a 1:1 field copy.
Enums§
- Cross
MemRel Check - Outcome of looking up a rel-type against a cross-mem entry in
the source schema’s
cross_mem_relationships:vocabulary.EdgeNotDeclaredcarries the recovery payload the engine layer wraps intocrate::EngineError::CrossMemEdgeNotDeclared; the other variants reuse the existingValidationErrorshapes so agents reading the wire shape decodeINVALID_REL_TYPE/INVALID_REL_SHAPEidentically in both intra- and cross-mem flows. - Relationship
Check - 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).
- Validation
Error - 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_unsetmay 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 (thetypediscriminator is re-seeded by the engine, never left absent).
Functions§
- is_
date_ shaped - Does
smatch the shape aDate-typed metadata value must have —YYYY-MM-DDor the ISO-8601 datetime formYYYY-MM-DDTHH:MM:SSZ? - missing_
required_ fields - Return one
MissingRequiredFieldper required metadata field that the caller did not supply and the schema does not auto-fill. A field is “auto-filled” when it carriesdefault_value,init_timestamp, orauto_timestamp— the engine writes a non-trivial value without caller input. Optional fields and supplied fields are skipped. - missing_
required_ sections - Return one
MissingRequiredSectionper required section that is absent or empty insections. 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
MetadataValuetype, consulting the schema for field-type information. Validates enum constraints when the field definition specifiesenum_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 declaredsource_types/target_typesfor this relationship. No-op when both constraint lists are empty (shape-free edges) or when the relationship name is unknown (callers run this only aftervalidate_rel_typesucceeds, so this branch is defensive). The target-type check is skipped whento_typeisNone— 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 incidentalUNKNOWN_METADATA_FIELDa reserved key would otherwise trip (no installable schema can declare one). Timestamp fields are NOT checked here: create’s posture forinit_timestamp/auto_timestampfields is stamp-and-proceed with anIGNORED_READONLY_FIELDwarning, 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
providedis either schema-declared forschema, or — if the schema has a catch-all section — admitted by it. Unknown keys returnValidationError::UnknownSectioncarrying the declared list plus a Levenshtein suggestion (or the catch-all key when no close match exists). - validate_
unsettable_ metadata_ key - Gate for
metadata_unsetkeys. 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_updateattempt to write a section that is either the virtualrelationshipssurface (managed bymemstead_relate) or not part of the type’supdatable_fieldsallowlist. 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 itsupdate_entitypath over themetadatamap: themem/id/typetriple stays engine-authoritative, and the schema’sinit_timestamp/auto_timestampannotations are honoured on write — the engine owns those values on create (init_timestamp, set once) and on every update (auto_timestamp, re-stamped). ReturnsValidationError::ReadOnlyFieldon rejection. The unset path has its own gate (validate_unsettable_metadata_key) because the reserved triple is unset-allowed there as the sanctioned repair.