Validation
Overview
Validation in the CEDAR Template Model consists of structural conformance to the abstract grammar and satisfaction of well-formedness conditions that are not expressed directly in grammar productions. The Canonical Validation Algorithm section defines a two-phase procedural algorithm that operationalises all normative rules in this document.
Contents
- Relationship to the wire-form error model
- Well-Formedness Conditions
- Canonical Validation Algorithm
- Open Questions
Relationship to the wire-form error model
This document and serialization.md §9 describe two complementary layers of conformance checking:
- The wire-form error model (
serialization.md§9) governs decoder and encoder behaviour at the JSON boundary. It defines three error categories (wireShape,lexical,structural) and a JSON-pointer-based path format for locating each error. - This validation algorithm governs post-decode checking on in-memory values. It assumes a successful decode has already produced syntactically well-formed structures and verifies the cross-cutting rules that bind those structures together (key uniqueness, cardinality, instance alignment, field-spec compatibility, and so on).
The two layers overlap in scope: many of the structural-invariant constraints listed in §9.1 are also Phase 1 checks here, because a conforming decoder operating in collected mode applies them at decode time. Implementations MAY perform validation entirely at decode (folding Phase 1 into the decoder) or entirely after decode (running Phase 1 as a separate pass). Either approach is conforming.
When this document refers to a constraint that is also enumerated in serialization.md §9.1, the wire-form error category and path semantics from §9 apply. Reported errors SHOULD use the four-field shape from §9.3 (category, path, production, message).
Well-Formedness Conditions
The conditions below are organised by structural concern. Each subsection corresponds to one of the §9.1 categories — primarily structural-invariant (cross-position constraints that the grammar alone cannot express) but with a few lexical constraints (regex-based well-formedness of pinned primitive types) called out where they are most natural to state.
EmbeddedArtifactKey Uniqueness
Within a single Template, each EmbeddedArtifact MUST have a unique EmbeddedArtifactKey. The uniqueness constraint is local to that template level and does not extend across nested template boundaries. Accordingly, an embedded template MAY contain EmbeddedArtifactKey values that are identical to keys used in its containing template, because each template defines its own local key space.
Each EmbeddedArtifactKey MUST conform to the AsciiIdentifier lexical form (per grammar.md) — the regular expression ^[A-Za-z][A-Za-z0-9_-]*$.
Embedding References
Each EmbeddedField MUST reference a Field.
Each EmbeddedTemplate MUST reference a Template.
Each EmbeddedPresentationComponent MUST reference a PresentationComponent.
Cardinality Consistency
If an embedding defines minimum and maximum cardinality, the minimum cardinality MUST NOT exceed the maximum cardinality.
ValueRequirement and Cardinality are orthogonal: ValueRequirement governs whether any values must be supplied at all; Cardinality governs the permitted count if values are supplied.
If an embedding is marked “required”, its minimum cardinality MUST be at least one. For EmbeddedTemplate, this means at least one NestedTemplateInstance keyed to that embedding MUST be present in the TemplateInstance.
If an embedding is marked “recommended”, absence of a value MUST NOT by itself cause conformance failure, though implementations MAY issue warnings or other authoring guidance.
If an embedding is marked “optional”, absence of a value MUST NOT by itself cause conformance failure.
If values are present for a “recommended” or “optional” embedding, their count MUST satisfy the Cardinality constraints of that embedding.
Cardinality Defaults and Multiplicity
When Cardinality is absent from an EmbeddedArtifact, the implied default cardinality is min_cardinality(1) with max_cardinality(1): the embedded artifact MUST appear exactly once.
An EmbeddedField is single-valued if its effective maximum cardinality is max_cardinality(1).
An EmbeddedField is multi-valued if its effective maximum cardinality is greater than one or is UnboundedCardinality.
Versioning
Version and ModelVersion MUST conform to the SemanticVersion lexical form (per grammar.md) — Semantic Versioning 2.0.0.
ModelVersion is a top-level component of every concrete Artifact (every Template, TemplateInstance, every Field, and every PresentationComponent); it is not a component of SchemaArtifactVersioning.
Status MUST be either draft or published.
SchemaArtifactVersioning.previousVersion and SchemaArtifactVersioning.derivedFrom, when both present on the same artifact, MUST NOT carry the same IRI value (per grammar.md §Schema Artifact Versioning).
Instance Alignment
Each FieldValue in a TemplateInstance MUST reference the EmbeddedArtifactKey of an EmbeddedField in the referenced Template.
Each NestedTemplateInstance in a TemplateInstance MUST reference the EmbeddedArtifactKey of an EmbeddedTemplate in the referenced Template.
TemplateInstance MUST NOT contain an InstanceValue for an EmbeddedPresentationComponent.
Field Spec Compatibility
Values in a FieldValue MUST satisfy the FieldSpec and any field-spec-specific properties of the referenced Field.
The contained values MUST follow the FieldSpec-to-Value correspondence defined in grammar.md:
| FieldSpec | Required Value type |
|---|---|
TextFieldSpec | TextValue |
IntegerNumberFieldSpec | IntegerNumberValue |
RealNumberFieldSpec | RealNumberValue |
BooleanFieldSpec | BooleanValue |
DateFieldSpec | DateValue (YearValue / YearMonthValue / FullDateValue per dateValueType) |
TimeFieldSpec | TimeValue |
DateTimeFieldSpec | DateTimeValue |
ControlledTermFieldSpec | ControlledTermValue |
SingleValuedEnumFieldSpec / MultiValuedEnumFieldSpec | EnumValue |
LinkFieldSpec | LinkValue |
EmailFieldSpec | EmailValue |
PhoneNumberFieldSpec | PhoneNumberValue |
OrcidFieldSpec | OrcidValue |
RorFieldSpec | RorValue |
DoiFieldSpec | DoiValue |
PubMedIdFieldSpec | PubMedIdValue |
RridFieldSpec | RridValue |
NihGrantIdFieldSpec | NihGrantIdValue |
AttributeValueFieldSpec | AttributeValue |
Additional well-formedness conditions apply per family, as described below.
For text values:
TextValueMUST carry a lexical form; it MAY carry a language tagTextFieldSpec.defaultValue, if present, MUST be aTextValue- if both
MinLengthandMaxLengthare present,MinLengthMUST NOT exceedMaxLength - if
MinLengthis present, eachTextValuelexical form MUST have length greater than or equal to that minimum - if
MaxLengthis present, eachTextValuelexical form MUST have length less than or equal to that maximum - if
ValidationRegexis present, eachTextValuelexical form MUST match that regular expression TextFieldSpec.defaultValue, if present, MUST satisfy any definedMinLength,MaxLength, andValidationRegexTextValuelexical forms SHOULD be in Unicode Normalization Form C- when present,
TextValue.langMUST be non-empty and well-formed according to BCP 47 - if
LangTagRequirementis“langTagRequired”, eachTextValueMUST carry alangslot - if
LangTagRequirementis“langTagForbidden”, eachTextValueMUST NOT carry alangslot TextFieldSpec.defaultValue, if present, MUST satisfy any definedLangTagRequirement
For integer-number values:
IntegerNumberValueMUST carry a base-10 integer lexical form; its datatype is implicitlyxsd:integer- if both
IntegerNumberMinValueandIntegerNumberMaxValueare present on the field spec,IntegerNumberMinValueMUST NOT exceedIntegerNumberMaxValue - if
IntegerNumberMinValueis present, eachIntegerNumberValueMUST be greater than or equal to that minimum - if
IntegerNumberMaxValueis present, eachIntegerNumberValueMUST be less than or equal to that maximum
For real-number values:
RealNumberValueMUST carry a real-valued lexical form together with aRealNumberDatatypeKind(one ofdecimal,float, ordouble)- a
RealNumberValue’s datatype MUST equal thedatatypedeclared on the enclosingRealNumberFieldSpec - if both
RealNumberMinValueandRealNumberMaxValueare present on the field spec,RealNumberMinValueMUST NOT exceedRealNumberMaxValue - if
RealNumberMinValueis present, eachRealNumberValueMUST be greater than or equal to that minimum - if
RealNumberMaxValueis present, eachRealNumberValueMUST be less than or equal to that maximum
For boolean values:
BooleanValueMUST carry a boolean payload; its datatype is implicitlyxsd:boolean
For date values:
DateFieldSpecwith dateValueType: “year” MUST useYearValue, whose lexical form MUST match the patternYYYY(a four-digit Gregorian year)DateFieldSpecwith dateValueType: “yearMonth” MUST useYearMonthValue, whose lexical form MUST match the patternYYYY-MM(with month in01–12)DateFieldSpecwith dateValueType: “fullDate” MUST useFullDateValue, whose lexical form MUST be a well-formedxsd:datelexical form (YYYY-MM-DDwith optional zone offset)DateFieldSpec.defaultValue, if present, MUST carry aDateValuearm consistent withdateValueType— dateValueType: “year” admits onlyYearValue, dateValueType: “yearMonth” admits onlyYearMonthValue, dateValueType: “fullDate” admits onlyFullDateValue. The same constraint applies toEmbeddedDateField.defaultValue.
For time values:
TimeValueMUST carry a well-formedxsd:timelexical formTimeFieldSpecvalues MUST conform to any statedTimePrecision
For date-time values:
DateTimeValueMUST carry a well-formedxsd:dateTimelexical formDateTimeFieldSpecvalues MUST conform to the statedDateTimeValueType
For enum values:
- A
FieldValuefor aSingleValuedEnumFieldSpecMUST contain exactly oneEnumValue - A
FieldValuefor aMultiValuedEnumFieldSpecMUST contain one or moreEnumValueconstructs (subject to theCardinalityof the embedding) - Each
EnumValue.value(aToken) MUST equal the canonicalTokenof one of the referenced spec’sPermissibleValueentries - The
Tokenstrings of anEnumFieldSpec’sPermissibleValue+MUST be unique within that spec SingleValuedEnumFieldSpec.defaultValue, if present, MUST be anEnumValuewhosevalueequals theTokenof one of itsPermissibleValueentriesMultiValuedEnumFieldSpec.defaultValues, if present, MUST be a (possibly empty) list ofEnumValueconstructs each whosevalueequals theTokenof one of itsPermissibleValueentries; the list MUST NOT contain duplicatevalueentries
An EnumValue matches a PermissibleValue if and only if the value’s Token string equals the permissible value’s Token string (compared character by character).
For controlled-term values:
ControlledTermValueMUST include a term identifier and SHOULD include a human-readable label
For contact values:
EmailValueMUST carry a non-empty lexical formPhoneNumberValueMUST carry a non-empty lexical form
For external authority values:
OrcidValueMUST include anOrcidIriRorValueMUST include aRorIriDoiValueMUST include aDoiIriPubMedIdValueMUST include aPubMedIriRridValueMUST include anRridIriNihGrantIdValueMUST include aNihGrantIri- these values MAY additionally include a human-readable
Label
For string-bearing values generally:
- lexical forms MUST be in Unicode Normalization Form C (per
serialization.md§4.5) - when present, language tags MUST conform to the
Bcp47Taglexical form (per grammar.md — RFC 5646)
For default values (both layers):
The model carries default values at two layers, and validation rules apply uniformly across the two:
- A field-level default lives on the reusable
Field’sFieldSpec(XxxFieldSpec.defaultValue), shared by every Template that embeds the field. Every concreteXxxFieldSpecexceptAttributeValueFieldSpecadmits an optional default. - An embedding-level default lives on the
EmbeddedXxxFieldinside a Template (EmbeddedXxxField.defaultValue), specific to that one embedding.
The well-formedness conditions:
- A default value, at either layer, MUST be the family-specific
Valuetype as given in grammar.md. - A default MUST satisfy every well-formedness condition that a corresponding
FieldValuewould satisfy for the sameFieldSpec(length bounds, numeric bounds, datatype consistency, lexical-form constraints, and so on). - Enum defaults at either layer MUST be
EnumValueconstructs (single forSingleValuedEnumField/Spec, a possibly-empty list forMultiValuedEnumField/Spec) whosevalueequals theTokenof one of the spec’sPermissibleValueentries; the multi-valued list MUST NOT contain duplicatevalueentries. - When both a field-level and an embedding-level default are present for the same field, the embedding-level default takes precedence (see grammar.md).
AttributeValueFieldSpecandEmbeddedAttributeValueFieldcarry no defaults at either layer.
For multiplicity:
- if an
EmbeddedFieldis single-valued, its correspondingFieldValueMUST NOT contain more than one value - if an
EmbeddedFieldis multi-valued, the number of values in itsFieldValueMUST satisfy the embedding cardinality constraints - if an
EmbeddedTemplatehas multiplicity greater than one, the number of correspondingNestedTemplateInstanceconstructs MUST satisfy the embedding cardinality constraints
Rendering Hint Compatibility
Any rendering hint used by the model MUST be compatible with the associated FieldSpec:
| Rendering hint | Permitted on |
|---|---|
TextRenderingHint | TextFieldSpec |
SingleValuedEnumRenderingHint | SingleValuedEnumFieldSpec |
MultiValuedEnumRenderingHint | MultiValuedEnumFieldSpec |
BooleanRenderingHint | BooleanFieldSpec |
NumericRenderingHint | IntegerNumberFieldSpec, RealNumberFieldSpec |
DateRenderingHint | DateFieldSpec |
TimeRenderingHint | TimeFieldSpec |
DateTimeRenderingHint | DateTimeFieldSpec |
Controlled Term Value Structure
If a value conforms to ControlledTermFieldSpec, the value MUST include a term identifier and SHOULD include a human-readable label.
A ControlledTermFieldSpec.defaultValue or EmbeddedControlledTermField.defaultValue, if present, SHOULD identify a term drawn from one of the declared ControlledTermSource entries of the referenced ControlledTermFieldSpec. Verifying source membership requires resolving the TermIri against an external ontology and is outside the scope of the canonical algorithm; see Out of Scope.
Canonical Validation Algorithm
The canonical validation algorithm consists of two phases that MUST be applied in order. Phase 1 validates the well-formedness of a Template and the artifacts it references. Phase 2 validates that a TemplateInstance conforms to a well-formed Template. Phase 2 MUST NOT be applied unless Phase 1 has passed without error.
Both phases are defined as error-collecting: all violations MUST be reported rather than stopping at the first failure. Implementations MAY additionally offer a fail-fast mode for performance, but the set of errors reported MUST be a subset of those that the collecting mode would report.
The algorithm is expressed as a set of named subroutines. Each subroutine takes typed inputs and produces a (possibly empty) set of errors. Verify denotes a hard constraint: failure produces an error. Warn denotes a SHOULD constraint: failure produces a warning. The notation count(X) denotes the number of elements of kind X, and len(s) denotes the length in characters of string s.
Reporting errors
Every Verify step in the algorithm has an associated error report that a conforming binding MUST surface on failure. Every Warn step has an associated warning report. Each step states its report inline as an On failure: line directly under the step.
Each report uses the four-field shape from serialization.md §9.3:
category— one ofwireShape,lexical, orstructural. Most validation reports arestructural(cross-position constraint); a few arelexical(regex / well-formedness of a primitive type).path— a JSON Pointer locating the offending slot in the wire form being validated.production— the wire-grammar production at the path.message— a human-readable explanation. The wording given in this document is recommended; bindings MAY use different text and SHOULD include enough detail to support diagnosis.
Path conventions. Subroutines describe paths relative to their input, using a placeholder for the input and slot accessors after slashes:
<input>— the subroutine’s input parameter, e.g.<embedded>,<template>,<fieldSpec>.<input>/slotName— a property slot.<input>/arrayName/<i>— an element of an array (with<i>an index variable).<input>/arrayName/<i>/inner— a nested slot inside the i-th element.
The caller of a subroutine substitutes the placeholder for the actual JSON Pointer of its input. For example, when validate_cardinality_consistency runs against template.members[2], an error reported at <embedded>/cardinality/min becomes /members/2/cardinality/min in the surfaced report.
When a subroutine S₁ calls another subroutine S₂ and S₂ reports an error at path <S₂.input>/foo, the surfaced path is <S₁.input>/<path-to-S₂.input>/foo. Each layer prepends its own input path. For example, validate_default_value calls a family-specific value-validator with the default value as input; an error from the inner validator at <value>/value is surfaced at <embedded>/defaultValue/value.
Warning reports follow the same shape but are emitted through the binding’s warning channel rather than its error channel.
External resolution
Several Verify steps require resolving an artifact-reference IRI to its definition — for example, validate_embedding_reference verifies that embedded.artifactRef “identifies an existing <Family>Field”. Resolution is outside the scope of this specification. A conforming validator is given an external resolver function
resolve(iri: Iri) → Artifact | null
that returns the artifact referenced by an IRI, or null if no such artifact is known. The validator MUST use this resolver to resolve every EmbeddedField.artifactRef, every EmbeddedTemplate.artifactRef, every EmbeddedPresentationComponent.artifactRef, and every TemplateInstance.templateRef.
How the resolver is implemented is a binding concern, not a model concern. Plausible implementations:
- A registry-backed resolver that looks up artifacts in a local catalogue.
- A document-local resolver that finds artifacts inlined in the same input document.
- A network-backed resolver that dereferences HTTP IRIs.
When resolve(iri) returns null, the surfaced error is:
structuralat the relevantartifactRefslot, production naming the embedding’s family, message“artifactRef does not resolve to an artifact”.
When resolve(iri) returns an artifact of the wrong family (e.g. a TextField is returned for an EmbeddedDateField.artifactRef), the surfaced error is the family-mismatch error already documented at validate_embedding_reference.
Implementations MAY operate without a resolver — in which case all Verify <…>identifying an existing <Family> steps are SKIPPED and any conformance claim must be qualified accordingly. This is a partial-validation mode appropriate for syntactic linting; full conformance requires a resolver.
Lexical-form precision
Several Verify steps appeal to lexical-form well-formedness for the primitive types pinned in grammar.md §Primitive String Types. For interoperability across implementations, the lexical-form predicates resolve as follows:
| Lexical form | Authoritative grammar |
|---|---|
SemanticVersion | The regular expression at semver.org. |
IriString | The IRI ABNF in RFC 3987 §2.2. The IRI MUST be absolute (carry a scheme). Implementations MAY use a permissive scheme-and-non-whitespace check as a fast pre-filter, but a conforming validator MUST be capable of full RFC 3987 conformance on demand. |
Bcp47Tag | The Language-Tag production of RFC 5646. Implementations MAY validate against the IANA Language Subtag Registry; a syntactic-only check is acceptable as a baseline. |
IntegerLexicalForm | Regex ^-?(0|[1-9][0-9]*)$. No leading +, no leading zeros (other than the literal 0), no whitespace. Magnitude is unbounded. |
AsciiIdentifier | Regex ^[A-Za-z][A-Za-z0-9_-]*$. Length is unbounded. |
Iso8601DateTimeLexicalForm | The dateTime lexical form from XML Schema 1.1 Part 2 §3.3.7, extended format. |
xsd:date lexical form | XML Schema 1.1 Part 2 §3.3.9. |
xsd:time lexical form | XML Schema 1.1 Part 2 §3.3.8. |
xsd:dateTime lexical form | XML Schema 1.1 Part 2 §3.3.7. |
xsd:decimal lexical form | XML Schema 1.1 Part 2 §3.3.3. |
xsd:float / xsd:double lexical form | XML Schema 1.1 Part 2 §3.3.6 and §3.3.5. The special values INF, -INF, and NaN are part of the lexical space. |
A conforming validator MUST treat the cited grammar as authoritative; a value is well-formed if and only if it matches the cited grammar. This pins the predicate so two independently-implemented validators agree on every input.
Phase 1: Schema Validation
Entry Point
validate_schema(template: Template)
Entry point for schema validation.
- Run
validate_model_version(template.model_version)andvalidate_schema_artifact_versioning(template.versioning). - If
template.template_rendering_hintis present: runvalidate_template_rendering_hint(template.template_rendering_hint). - Let
fields= the set ofFieldartifacts referenced byEmbeddedFieldconstructs intemplate. - For each
fieldinfields: runvalidate_model_version(field.model_version),validate_schema_artifact_versioning(field.versioning), andvalidate_field_spec(field.field_spec). - Let
pcs= the set ofPresentationComponentartifacts referenced byEmbeddedPresentationComponentconstructs intemplate. - For each
componentinpcs: runvalidate_model_version(component.model_version).PresentationComponentdoes not carrySchemaArtifactVersioning, so no versioning validation step applies. - Run
validate_embedded_artifact_keys(template). - For each
embeddedintemplate.embedded_artifacts:- Run
validate_embedding_reference(embedded). - Run
validate_cardinality_consistency(embedded). - If
embeddedis anEmbeddedField: runvalidate_rendering_hints(embedded). - If
embedded.default_valueis present: runvalidate_default_value(embedded.default_value, embedded). - If
embeddedis anEmbeddedTemplate: runvalidate_schema(embedded.referenced_template).
- Run
Metadata and Key Validation
validate_schema_artifact_versioning(versioning: SchemaArtifactVersioning)
Applies the Versioning rules to the SchemaArtifactVersioning slot carried by each schema artifact (Template, Field). PresentationComponent and TemplateInstance do not carry SchemaArtifactVersioning; this subroutine is not invoked for them.
- Let
version=versioning.version. Verifyversionconforms to theSemanticVersionlexical form (Semantic Versioning 2.0.0).On failure- category
lexical- path
<versioning>/version- production
SchemaArtifactVersioning- message
"version is not a valid SemanticVersion 2.0.0 string"
- Let
status=versioning.status. Verify status ∈ { draft, published }.On failure- category
wireShape- path
<versioning>/status- production
SchemaArtifactVersioning- message
"status must be 'draft' or 'published'"
- If both
versioning.previous_versionandversioning.derived_fromare present: verify they do not carry the same IRI value.On failure- category
structural- path
<versioning>/derivedFrom- production
SchemaArtifactVersioning- message
"previousVersion and derivedFrom MUST NOT carry the same IRI"
validate_template_rendering_hint(hint: TemplateRenderingHint)
- If
hint.help_display_modeis present: verify it is one of“inline”,“tooltip”,“both”,“none”.On failure- category
wireShape- path
<hint>/helpDisplayMode- production
HelpDisplayMode- message
"unknown HelpDisplayMode value"
validate_model_version(modelVersion: ModelVersion)
Applies the Versioning rules to the artifact-level ModelVersion carried directly by every concrete Artifact.
- Verify
modelVersionconforms to theSemanticVersionlexical form (Semantic Versioning 2.0.0).On failure- category
lexical- path
<modelVersion>- production
- naming the enclosing artifact (e.g.
TextField,Template) - message
"modelVersion is not a valid SemanticVersion 2.0.0 string"
validate_embedded_artifact_keys(template: Template)
Applies the EmbeddedArtifactKey Uniqueness rules.
- Let
keys= the sequence ofEmbeddedArtifactKeyvalues across allEmbeddedArtifactconstructs intemplate. - For each key
kinkeys: verifykconforms to theAsciiIdentifierlexical form (regex^[A-Za-z][A-Za-z0-9_-]*$).On failure- category
lexical- path
<template>/members/<i>/key- production
- naming the embedded artifact at index
<i> - message
"EmbeddedArtifactKey does not match the AsciiIdentifier pattern"
- Verify all values in
keysare distinct: for each pair (k₁, k₂) where k₁ ≠ k₂ as positions but k₁ = k₂ as values, report a duplicate-key error. Key uniqueness is scoped totemplate; the same key may appear in a nested template without conflict.On failure- category
structural- path
<template>/members/<j>/key(the second occurrence)- production
Template- message
"EmbeddedArtifact.key is not unique within the enclosing Template (also at /members/<i>/key)"
Reference and Cardinality Validation
validate_embedding_reference(embedded: EmbeddedArtifact)
Applies the Embedding References rules.
Each step below resolves embedded.artifactRef via the external resolver resolve(iri) (see External resolution) and verifies the resolved artifact’s family. If the validator was given no resolver, all steps are SKIPPED.
For each step below, two failure modes are possible:
- category
structural- path
<embedded>/artifactRef- production
- naming
embedded's family - message
"artifactRef does not resolve to an artifact"
- category
structural- path
<embedded>/artifactRef- production
- naming
embedded's family - message
"artifactRef resolves to an artifact of the wrong family (expected <Family>, got <ResolvedFamily>)"
- If
embeddedis anEmbeddedTextField: verifyembedded.artifactRefis aTextFieldIdidentifying an existingTextField. - If
embeddedis anEmbeddedIntegerNumberField: verifyembedded.artifactRefis anIntegerNumberFieldIdidentifying an existingIntegerNumberField. - If
embeddedis anEmbeddedRealNumberField: verifyembedded.artifactRefis aRealNumberFieldIdidentifying an existingRealNumberField. - If
embeddedis anEmbeddedBooleanField: verifyembedded.artifactRefis aBooleanFieldIdidentifying an existingBooleanField. - If
embeddedis anEmbeddedDateField: verifyembedded.artifactRefis aDateFieldIdidentifying an existingDateField. - If
embeddedis anEmbeddedTimeField: verifyembedded.artifactRefis aTimeFieldIdidentifying an existingTimeField. - If
embeddedis anEmbeddedDateTimeField: verifyembedded.artifactRefis aDateTimeFieldIdidentifying an existingDateTimeField. - If
embeddedis anEmbeddedControlledTermField: verifyembedded.artifactRefis aControlledTermFieldIdidentifying an existingControlledTermField. - If
embeddedis anEmbeddedSingleValuedEnumField: verifyembedded.artifactRefis aSingleValuedEnumFieldIdidentifying an existingSingleValuedEnumField. - If
embeddedis anEmbeddedMultiValuedEnumField: verifyembedded.artifactRefis aMultiValuedEnumFieldIdidentifying an existingMultiValuedEnumField. - If
embeddedis anEmbeddedLinkField: verifyembedded.artifactRefis aLinkFieldIdidentifying an existingLinkField. - If
embeddedis anEmbeddedEmailField: verifyembedded.artifactRefis anEmailFieldIdidentifying an existingEmailField. - If
embeddedis anEmbeddedPhoneNumberField: verifyembedded.artifactRefis aPhoneNumberFieldIdidentifying an existingPhoneNumberField. - If
embeddedis anEmbeddedOrcidField: verifyembedded.artifactRefis anOrcidFieldIdidentifying an existingOrcidField. - If
embeddedis anEmbeddedRorField: verifyembedded.artifactRefis aRorFieldIdidentifying an existingRorField. - If
embeddedis anEmbeddedDoiField: verifyembedded.artifactRefis aDoiFieldIdidentifying an existingDoiField. - If
embeddedis anEmbeddedPubMedIdField: verifyembedded.artifactRefis aPubMedIdFieldIdidentifying an existingPubMedIdField. - If
embeddedis anEmbeddedRridField: verifyembedded.artifactRefis anRridFieldIdidentifying an existingRridField. - If
embeddedis anEmbeddedNihGrantIdField: verifyembedded.artifactRefis aNihGrantIdFieldIdidentifying an existingNihGrantIdField. - If
embeddedis anEmbeddedAttributeValueField: verifyembedded.artifactRefis anAttributeValueFieldIdidentifying an existingAttributeValueField. - If
embeddedis anEmbeddedTemplate: verifyembedded.artifactRefis aTemplateIdidentifying an existingTemplate. - If
embeddedis anEmbeddedPresentationComponent: verifyembedded.artifactRefis aPresentationComponentIdidentifying an existingPresentationComponent.
validate_cardinality_consistency(embedded: EmbeddedArtifact)
Applies the Cardinality Consistency rules.
- Let
min=embedded.cardinality.min_cardinalityifembedded.cardinalityis present, else1. - Let
max=embedded.cardinality.max_cardinalityifembedded.cardinalityis present, else1. IfmaxisUnboundedCardinality, let max = ∞. - Verify min ≤ max.
On failure
- category
structural- path
<embedded>/cardinality- production
Cardinality- message
"min must not exceed max"
- Let
req=embedded.value_requirementif present, else“optional”. - If req = “required”: verify min ≥ 1.
On failure
- category
structural- path
<embedded>/cardinality/min- production
Cardinality- message
"required embedding must have min cardinality of at least 1"
Field Spec Validation
Applies the Field Spec Compatibility rules. See also Field Specs in the abstract grammar.
validate_field_spec(fieldSpec: FieldSpec)
Dispatch on the kind of fieldSpec:
- If
fieldSpecisTextFieldSpec: runvalidate_text_field_spec(fieldSpec). - If
fieldSpecisIntegerNumberFieldSpec: runvalidate_integer_number_field_spec(fieldSpec). - If
fieldSpecisRealNumberFieldSpec: runvalidate_real_number_field_spec(fieldSpec). - If
fieldSpecisSingleValuedEnumFieldSpecorMultiValuedEnumFieldSpec: runvalidate_enum_field_spec(fieldSpec). - All other field specs have no additional schema-level well-formedness checks beyond structural grammar conformance.
validate_text_field_spec(fieldSpec: TextFieldSpec)
- If both
fieldSpec.min_lengthandfieldSpec.max_lengthare present: verify fieldSpec.min_length ≤ fieldSpec.max_length.On failure- category
structural- path
<fieldSpec>/minLength- production
TextFieldSpec- message
"minLength must not exceed maxLength"
- If
fieldSpec.lang_tag_requirementis present: verify it is one of“langTagRequired”,“langTagOptional”,“langTagForbidden”.On failure- category
wireShape- path
<fieldSpec>/langTagRequirement- production
LangTagRequirement- message
"unknown LangTagRequirement value"
validate_integer_number_field_spec(fieldSpec: IntegerNumberFieldSpec)
- If both
fieldSpec.min_valueandfieldSpec.max_valueare present: verify fieldSpec.min_value ≤ fieldSpec.max_value.On failure- category
structural- path
<fieldSpec>/minValue- production
IntegerNumberFieldSpec- message
"minValue must not exceed maxValue"
validate_real_number_field_spec(fieldSpec: RealNumberFieldSpec)
- If both
fieldSpec.min_valueandfieldSpec.max_valueare present: verify fieldSpec.min_value ≤ fieldSpec.max_value.On failure- category
structural- path
<fieldSpec>/minValue- production
RealNumberFieldSpec- message
"minValue must not exceed maxValue"
validate_enum_field_spec(fieldSpec: EnumFieldSpec)
- Let
tokens= the sequence ofpv.valuevalues across allpvinfieldSpec.permissible_values. - Verify all values in
tokensare distinct: report a duplicate-token error for any pair sharing the same token string.On failure- category
structural- path
<fieldSpec>/permissibleValues/<j>/value(the second occurrence)- production
- naming
fieldSpec's kind - message
"PermissibleValue.value is not unique within the enclosing spec (also at /permissibleValues/<i>/value)"
- For each
pvinfieldSpec.permissible_values: verifypv.valueis a non-empty Unicode string.On failure- category
wireShape- path
<fieldSpec>/permissibleValues/<i>/value- production
PermissibleValue- message
"value must be a non-empty Unicode string"
- For each
pvinfieldSpec.permissible_values, for eachminpv.meanings: verifym.iriis a syntactically valid IRI.On failure- category
lexical- path
<fieldSpec>/permissibleValues/<i>/meanings/<j>/iri- production
Meaning- message
"iri is not a valid IRI"
- If
fieldSpecis aSingleValuedEnumFieldSpecandfieldSpec.default_valueis present: verifyfieldSpec.default_valueis anEnumValueand that fieldSpec.default_value.value ∈ tokens.On failure- category
structural- path
<fieldSpec>/defaultValue/value- production
SingleValuedEnumFieldSpec- message
"defaultValue does not match any of the spec's permissibleValues"
- If
fieldSpecis aMultiValuedEnumFieldSpecandfieldSpec.default_valuesis present:- Verify each entry is an
EnumValueand that its value ∈ tokens.On failure- category
structural- path
<fieldSpec>/defaultValues/<i>/value- production
MultiValuedEnumFieldSpec- message
"defaultValues entry does not match any of the spec's permissibleValues"
- Verify all entries’
valuestrings are distinct.On failure- category
structural- path
<fieldSpec>/defaultValues/<j>/value(the second occurrence)- production
MultiValuedEnumFieldSpec- message
"defaultValues contains duplicate entries (also at /defaultValues/<i>/value)"
- Verify each entry is an
Default Value Validation
validate_default_value(defaultValue: Value, embedded: EmbeddedArtifact)
Let fieldSpec = the FieldSpec of the Field referenced by embedded.
- Verify
defaultValueis of the family-specificValuetype forfieldSpec:TextValueforTextFieldSpec,IntegerNumberValueforIntegerNumberFieldSpec,RealNumberValueforRealNumberFieldSpec,BooleanValueforBooleanFieldSpec,DateValueforDateFieldSpec,TimeValueforTimeFieldSpec,DateTimeValueforDateTimeFieldSpec,ControlledTermValueforControlledTermFieldSpec,EnumValueforSingleValuedEnumFieldSpec, a sequence ofEnumValueforMultiValuedEnumFieldSpec,LinkValueforLinkFieldSpec,EmailValueforEmailFieldSpec,PhoneNumberValueforPhoneNumberFieldSpec, and the corresponding external-authorityValuetypes for the external-authority field specs.AttributeValueFieldSpecdoes not admit a default value.On failure- category
wireShape- path
<embedded>/defaultValue- production
- naming
embedded's family - message
"defaultValue must be a <FamilyValue> (got <kind>)"
- Apply the family-specific validate_xxx_value(defaultValue, fieldSpec) procedure to
defaultValue. The default value MUST satisfy every constraint that aFieldValuecarrying the sameValuewould satisfy. Errors reported by the inner subroutine are surfaced verbatim, with the path rooted at<embedded>/defaultValue. - If
embeddedis anEmbeddedSingleValuedEnumField: verifydefaultValueis a singleEnumValue(not a sequence).On failure- category
wireShape- path
<embedded>/defaultValue- production
EmbeddedSingleValuedEnumField- message
"defaultValue must be a single EnumValue, not a sequence"
- If
embeddedis anEmbeddedMultiValuedEnumField: verifydefaultValueis a (possibly empty) sequence ofEnumValueconstructs and that no two entries share the samevalue.On failure (shape)- category
wireShape- path
<embedded>/defaultValue- production
EmbeddedMultiValuedEnumField- message
"defaultValue must be an array of EnumValue"
On failure (duplicate)- category
structural- path
<embedded>/defaultValue/<j>/value(the second occurrence)- production
EmbeddedMultiValuedEnumField- message
"defaultValue contains duplicate entries (also at /defaultValue/<i>/value)"
Rendering Hint Validation
validate_rendering_hints(embedded: EmbeddedField)
Applies the Rendering Hint Compatibility rules.
Let fieldSpec = the FieldSpec of the Field referenced by embedded.
For each step below, on failure: structural at the rendering-hint slot’s path (e.g. <embedded>/renderingHint), production naming embedded’s family, message “<HintKind> is not compatible with <FieldSpecKind>”.
- If
embeddedcarries aTextRenderingHint: verifyfieldSpecisTextFieldSpec. - If
embeddedcarries aSingleValuedEnumRenderingHint: verifyfieldSpecisSingleValuedEnumFieldSpec. - If
embeddedcarries aMultiValuedEnumRenderingHint: verifyfieldSpecisMultiValuedEnumFieldSpec. - If
embeddedcarries aNumericRenderingHint: verifyfieldSpecisIntegerNumberFieldSpecorRealNumberFieldSpec. - If
embeddedcarries aDateRenderingHint: verifyfieldSpecisDateFieldSpec. - If
embeddedcarries aTimeRenderingHint: verifyfieldSpecisTimeFieldSpec. - If
embeddedcarries aDateTimeRenderingHint: verifyfieldSpecisDateTimeFieldSpec.
Phase 2: Instance Validation
Entry Point
validate_instance(instance: TemplateInstance, template: Template)
Entry point for instance validation.
- Run
validate_model_version(instance.model_version). - Run
validate_instance_alignment(instance, template). - Run
validate_field_presence_and_cardinality(instance, template). - For each
fieldValueininstance.instance_valueswherefieldValueis aFieldValue:- Let
embeddedField= theEmbeddedFieldintemplatewhose key =fieldValue.key. - Run
validate_field_value(fieldValue, embeddedField).
- Let
- Run
validate_nested_template_presence_and_cardinality(instance, template). - For each
nestedInstanceininstance.instance_valueswherenestedInstanceis aNestedTemplateInstance:- Let
embeddedTemplate= theEmbeddedTemplateintemplatewhose key =nestedInstance.key. - Let
referencedTemplate= theTemplateidentified byembeddedTemplate.artifactRef. - Run
validate_instance(nestedInstance, referencedTemplate).
- Let
Structural Alignment
validate_instance_alignment(instance: TemplateInstance, template: Template)
Applies the Instance Alignment rules.
- Let
field_keys= { embedded.key | embedded ∈ template.embedded_artifacts, embedded is EmbeddedField }. - Let
template_keys= { embedded.key | embedded ∈ template.embedded_artifacts, embedded is EmbeddedTemplate }. - Let
pc_keys= { embedded.key | embedded ∈ template.embedded_artifacts, embedded is EmbeddedPresentationComponent }. - For each
fieldValueininstance.instance_valueswherefieldValueis aFieldValue: verify fieldValue.key ∈ field_keys.On failure- category
structural- path
<instance>/values/<i>/key- production
FieldValue- message
"FieldValue.key does not identify any EmbeddedField in the referenced Template"
- For each
nestedInstanceininstance.instance_valueswherenestedInstanceis aNestedTemplateInstance: verify nestedInstance.key ∈ template_keys.On failure- category
structural- path
<instance>/values/<i>/key- production
NestedTemplateInstance- message
"NestedTemplateInstance.key does not identify any EmbeddedTemplate in the referenced Template"
- For each
instanceValueininstance.instance_values: verify instanceValue.key ∉ pc_keys.On failure- category
structural- path
<instance>/values/<i>/key- production
- naming
instanceValue's kind - message
"InstanceValue keyed to an EmbeddedPresentationComponent — presentation components do not produce instance values"
Field Presence and Cardinality
validate_field_presence_and_cardinality(instance: TemplateInstance, template: Template)
Applies the Cardinality Consistency and Cardinality Defaults and Multiplicity rules.
For each embeddedField in template.embedded_artifacts where embeddedField is an EmbeddedField:
- Let
eff_min=embeddedField.cardinality.min_cardinalityif present, else1. - Let
eff_max=embeddedField.cardinality.max_cardinalityif present, else1. Ifeff_maxisUnboundedCardinality, let eff_max = ∞. - Let
req=embeddedField.value_requirementif present, else“optional”. - Let
fieldValue= theFieldValueininstancewith key =embeddedField.key, orabsentif none exists. - If req = “required”:
- Verify fieldValue ≠ absent.
On failure
- category
structural- path
<instance>/values- production
TemplateInstance- message
"required field <embeddedField.key> is missing from the instance"
- Verify count(fieldValue.values) ≥ eff_min.
On failure
- category
structural- path
<fieldValue>/values- production
FieldValue- message
"value count below required minimum cardinality (got <n>, expected ≥ <eff_min>)"
- If eff_max ≠ ∞: verify count(fieldValue.values) ≤ eff_max.
On failure
- category
structural- path
<fieldValue>/values- production
FieldValue- message
"value count above maximum cardinality (got <n>, expected ≤ <eff_max>)"
- Verify fieldValue ≠ absent.
- If req = “recommended” or req = “optional”:
- If fieldValue ≠ absent:
- Verify count(fieldValue.values) ≥ eff_min.
On failure
- category
structural- path
<fieldValue>/values- production
FieldValue- message
"value count below minimum cardinality (got <n>, expected ≥ <eff_min>)"
- If eff_max ≠ ∞: verify count(fieldValue.values) ≤ eff_max.
On failure
- category
structural- path
<fieldValue>/values- production
FieldValue- message
"value count above maximum cardinality (got <n>, expected ≤ <eff_max>)"
- Verify count(fieldValue.values) ≥ eff_min.
- If fieldValue ≠ absent:
Field Value Validation
validate_field_value(fieldValue: FieldValue, embeddedField: EmbeddedField)
- Let
fieldSpec= theFieldSpecof theFieldreferenced byembeddedField. - For each
valueinfieldValue.values: runvalidate_value(value, fieldSpec).
validate_value(value: Value, fieldSpec: FieldSpec)
Dispatch on the kind of fieldSpec:
TextFieldSpec→validate_text_value(value, fieldSpec)IntegerNumberFieldSpec→validate_integer_number_value(value, fieldSpec)RealNumberFieldSpec→validate_real_number_value(value, fieldSpec)BooleanFieldSpec→validate_boolean_value(value, fieldSpec)DateFieldSpec→validate_date_value(value, fieldSpec)TimeFieldSpec→validate_time_value(value, fieldSpec)DateTimeFieldSpec→validate_datetime_value(value, fieldSpec)ControlledTermFieldSpec→validate_controlled_term_value(value, fieldSpec)SingleValuedEnumFieldSpecorMultiValuedEnumFieldSpec→validate_enum_value(value, fieldSpec)LinkFieldSpec→validate_link_value(value)EmailFieldSpecorPhoneNumberFieldSpec→validate_contact_value(value)OrcidFieldSpec,RorFieldSpec,DoiFieldSpec,PubMedIdFieldSpec,RridFieldSpec, orNihGrantIdFieldSpec→validate_external_authority_value(value, fieldSpec)AttributeValueFieldSpec→validate_attribute_value(value)
validate_text_value(value: TextValue, fieldSpec: TextFieldSpec)
- Let
lexicalForm=value.value. - If
fieldSpec.min_lengthis present: verify len(lexicalForm) ≥ fieldSpec.min_length.On failure- category
structural- path
<value>/value- production
TextValue- message
"value length below TextFieldSpec.minLength"
- If
fieldSpec.max_lengthis present: verify len(lexicalForm) ≤ fieldSpec.max_length.On failure- category
structural- path
<value>/value- production
TextValue- message
"value length above TextFieldSpec.maxLength"
- If
fieldSpec.validation_regexis present: verifylexicalFormmatchesfieldSpec.validation_regex.On failure- category
structural- path
<value>/value- production
TextValue- message
"value does not match TextFieldSpec.validationRegex"
- If
value.langis present: verify it conforms to theBcp47Taglexical form (RFC 5646).On failure- category
lexical- path
<value>/lang- production
TextValue- message
"lang is not a well-formed BCP 47 tag"
- If fieldSpec.lang_tag_requirement = “langTagRequired”: verify
value.langis present.On failure- category
structural- path
<value>/lang- production
TextValue- message
"lang tag missing; TextFieldSpec.langTagRequirement is 'langTagRequired'"
- If fieldSpec.lang_tag_requirement = “langTagForbidden”: verify
value.langis absent.On failure- category
structural- path
<value>/lang- production
TextValue- message
"lang tag present; TextFieldSpec.langTagRequirement is 'langTagForbidden'"
validate_integer_number_value(value: IntegerNumberValue, fieldSpec: IntegerNumberFieldSpec)
- Verify
value.valueconforms to theIntegerLexicalForm(regex^-?(0|[1-9][0-9]*)$). Letn= its integer value.On failure- category
lexical- path
<value>/value- production
IntegerNumberValue- message
"value is not a well-formed IntegerLexicalForm"
- If
fieldSpec.min_valueis present: verify n ≥ fieldSpec.min_value.value (compared as integers).On failure- category
structural- path
<value>/value- production
IntegerNumberValue- message
"value below IntegerNumberFieldSpec.minValue"
- If
fieldSpec.max_valueis present: verify n ≤ fieldSpec.max_value.value (compared as integers).On failure- category
structural- path
<value>/value- production
IntegerNumberValue- message
"value above IntegerNumberFieldSpec.maxValue"
validate_real_number_value(value: RealNumberValue, fieldSpec: RealNumberFieldSpec)
- Verify value.datatype = fieldSpec.datatype (one of
decimal,float,double).On failure- category
structural- path
<value>/datatype- production
RealNumberValue- message
"datatype does not match the enclosing RealNumberFieldSpec.datatype"
- Verify
value.valueis a well-formed lexical form for that datatype. Letn= its numeric value.On failure- category
lexical- path
<value>/value- production
RealNumberValue- message
"value is not a well-formed lexical form for datatype <datatype>"
- If
fieldSpec.min_valueis present: verify n ≥ fieldSpec.min_value.value (compared as numbers underfieldSpec.datatype’s ordering).On failure- category
structural- path
<value>/value- production
RealNumberValue- message
"value below RealNumberFieldSpec.minValue"
- If
fieldSpec.max_valueis present: verify n ≤ fieldSpec.max_value.value (compared as numbers underfieldSpec.datatype’s ordering).On failure- category
structural- path
<value>/value- production
RealNumberValue- message
"value above RealNumberFieldSpec.maxValue"
Comparison semantics for float and double. The numeric value n MAY be NaN, +INF, or -INF (these are part of the xsd:float and xsd:double lexical spaces). The bound comparisons in steps 3 and 4 follow IEEE 754 ordering:
- If
nisNaN, every comparison n ≥ x and n ≤ x is false. ANaNvalue therefore violates any presentminValueormaxValuebound and reports the corresponding bound-failure error. - If
nis+INF, then n ≥ x is true for every finitexand n ≤ x is true only whenxis+INF. - If
nis-INF, then n ≤ x is true for every finitexand n ≥ x is true only whenxis-INF.
This convention matches the IEEE 754 totalOrder relation restricted to comparison; bindings SHOULD use their host language’s IEEE 754-compliant comparison primitives.
validate_boolean_value(value: BooleanValue, fieldSpec: BooleanFieldSpec)
- Verify
value.valueistrueorfalse.On failure- category
wireShape- path
<value>/value- production
BooleanValue- message
"value must be a JSON boolean"
validate_date_value(value: DateValue, fieldSpec: DateFieldSpec)
- If fieldSpec.date_value_type = “year”: verify
valueis aYearValuewhosevaluematches[0-9]{4}.On failure (arm)- category
structural- path
<value>- production
DateValue- message
"DateFieldSpec.dateValueType 'year' admits only YearValue"
On failure (lexical)- category
lexical- path
<value>/value- production
YearValue- message
"value does not match YYYY"
- If fieldSpec.date_value_type = “yearMonth”: verify
valueis aYearMonthValuewhosevaluematches[0-9]{4}-(0[1-9]|1[0-2]).On failure (arm)- category
structural- path
<value>- production
DateValue- message
"DateFieldSpec.dateValueType 'yearMonth' admits only YearMonthValue"
On failure (lexical)- category
lexical- path
<value>/value- production
YearMonthValue- message
"value does not match YYYY-MM"
- If fieldSpec.date_value_type = “fullDate”: verify
valueis aFullDateValuewhosevalueis a well-formedxsd:datelexical form.On failure (arm)- category
structural- path
<value>- production
DateValue- message
"DateFieldSpec.dateValueType 'fullDate' admits only FullDateValue"
On failure (lexical)- category
lexical- path
<value>/value- production
FullDateValue- message
"value is not a well-formed xsd:date lexical form"
validate_time_value(value: TimeValue, fieldSpec: TimeFieldSpec)
For each step below that verifies a precision constraint, on failure: structural at <value>/value, production TimeValue, message “value does not match the precision required by TimeFieldSpec.timePrecision”. For lexical-form failures (xsd:time ill-formedness), the category is lexical instead.
- Let
t=value.value. - If fieldSpec.time_precision = “hourMinute”: verify
tcontains only hour and minute components (formHH:MM; no seconds or fractional seconds present). - If fieldSpec.time_precision = “hourMinuteSecond”: verify
tcontains hour, minute, and second components (formHH:MM:SS; no fractional seconds present). - If fieldSpec.time_precision = “hourMinuteSecondFraction”: verify
tis a well-formedxsd:timelexical form; fractional seconds are permitted. - If
fieldSpec.time_precisionis absent: verifytis a well-formedxsd:timelexical form. - If fieldSpec.timezone_requirement = “timezoneRequired”: verify
tincludes a timezone designator.On failure- category
structural- path
<value>/value- production
TimeValue- message
"timezone designator missing; TimeFieldSpec.timezoneRequirement is 'timezoneRequired'"
validate_datetime_value(value: DateTimeValue, fieldSpec: DateTimeFieldSpec)
For each step below that verifies a precision constraint, on failure: structural at <value>/value, production DateTimeValue, message “value does not match the precision required by DateTimeFieldSpec.dateTimeValueType”. For lexical-form failures (xsd:dateTime ill-formedness), the category is lexical instead.
- Let
dt=value.value. - If fieldSpec.datetime_value_type = “dateHourMinute”: verify the time component of
dtcontains only hour and minute (form…THH:MM; no seconds present). - If fieldSpec.datetime_value_type = “dateHourMinuteSecond”: verify the time component contains hour, minute, and second (form
…THH:MM:SS; no fractional seconds present). - If fieldSpec.datetime_value_type = “dateHourMinuteSecondFraction”: verify
dtis a well-formedxsd:dateTimelexical form; fractional seconds are permitted. - If fieldSpec.timezone_requirement = “timezoneRequired”: verify
dtincludes a timezone designator.On failure- category
structural- path
<value>/value- production
DateTimeValue- message
"timezone designator missing; DateTimeFieldSpec.timezoneRequirement is 'timezoneRequired'"
validate_controlled_term_value(value: ControlledTermValue, fieldSpec: ControlledTermFieldSpec)
- Verify
value.term_iriis present.On failure- category
wireShape- path
<value>/term- production
ControlledTermValue- message
"term is required"
- Warn if
value.labelis absent.On warning- category
structural- path
<value>/label- production
ControlledTermValue- message
"label SHOULD be present so consumers without ontology access can render the term"
Note: validation of value.term_iri against fieldSpec.controlled_term_sources requires an external ontology resolver and is outside the scope of this algorithm; see Out of Scope.
validate_enum_value(value: EnumValue, fieldSpec: EnumFieldSpec)
- Verify there exists a
pvinfieldSpec.permissible_valuessuch that value.value = pv.value (string equality, character by character).On failure- category
structural- path
<value>/value- production
EnumValue- message
"value does not match any of the spec's permissibleValues tokens"
validate_link_value(value: LinkValue)
- Verify
value.iriis present and is a well-formed IRI.On failure (missing)- category
wireShape- path
<value>/iri- production
LinkValue- message
"iri is required"
On failure (malformed)- category
lexical- path
<value>/iri- production
LinkValue- message
"iri is not a valid IRI"
validate_contact_value(value: ContactValue)
- If
valueis anEmailValue: verifyvalue.valueis a non-empty lexical form.On failure- category
wireShape- path
<value>/value- production
EmailValue- message
"value must be a non-empty string"
- If
valueis aPhoneNumberValue: verifyvalue.valueis a non-empty lexical form.On failure- category
wireShape- path
<value>/value- production
PhoneNumberValue- message
"value must be a non-empty string"
validate_external_authority_value(value: ExternalAuthorityValue, fieldSpec: ExternalAuthorityFieldSpec)
Each external-authority Value carries a typed IRI specialised for its authority. The lexical patterns below are recommended (suitable for syntactic conformance checking) but are not structurally normative beyond Iri well-formedness; binding-level validators MAY apply stricter checks.
| Field spec | Required IRI | Recommended pattern |
|---|---|---|
OrcidFieldSpec | OrcidIri | https://orcid\.org/\d{4}-\d{4}-\d{4}-\d{3}[0-9X] |
RorFieldSpec | RorIri | https://ror\.org/0[a-hj-km-np-tv-z0-9]{6}[0-9]{2} |
DoiFieldSpec | DoiIri | https://doi\.org/10\.\d{4,9}/.+ |
PubMedIdFieldSpec | PubMedIri | https://pubmed\.ncbi\.nlm\.nih\.gov/\d+ |
RridFieldSpec | RridIri | https://identifiers\.org/RRID:[A-Z]+_\d+ |
NihGrantIdFieldSpec | NihGrantIri | (see Out of Scope) |
In every case the procedure is: verify value is the corresponding XxxValue and that its iri slot is present and is a well-formed Iri per grammar.md §Primitive String Types. Implementations MAY additionally check the recommended pattern.
- category
wireShape- path
<value>/iri- production
- naming
value's family - message
"iri is required"
- category
lexical- path
<value>/iri- production
- naming
value's family - message
"iri is not a valid Iri"
- category
lexical- path
<value>/iri- production
- naming
value's family - message
"iri does not match the recommended pattern for <Authority>"
validate_attribute_value(value: AttributeValue)
- Verify
value.nameis present and contains a non-emptystring.On failure- category
wireShape- path
<value>/name- production
AttributeValue- message
"name must be a non-empty string"
- Verify
value.valueis present and is a well-formedValue.On failure- category
wireShape- path
<value>/value- production
AttributeValue- message
"value is required and must be a Value"
- If
value.valueis anAttributeValue: runvalidate_attribute_value(value.value).
Nested Template Validation
validate_nested_template_presence_and_cardinality(instance: TemplateInstance, template: Template)
Applies the Cardinality Consistency and Cardinality Defaults and Multiplicity rules.
For each embeddedTemplate in template.embedded_artifacts where embeddedTemplate is an EmbeddedTemplate:
- Let
eff_min=embeddedTemplate.cardinality.min_cardinalityif present, else1. - Let
eff_max=embeddedTemplate.cardinality.max_cardinalityif present, else1. Ifeff_maxisUnboundedCardinality, let eff_max = ∞. - Let
req=embeddedTemplate.value_requirementif present, else“optional”. - Let
n= count({ nestedInstance | nestedInstance ∈ instance.instance_values, nestedInstance is NestedTemplateInstance, nestedInstance.key = embeddedTemplate.key }). - If req = “required”:
- Verify n ≥ eff_min.
On failure
- category
structural- path
<instance>/values- production
TemplateInstance- message
"required NestedTemplateInstance count below minimum (got <n>, expected ≥ <eff_min>) for key '<embeddedTemplate.key>'"
- If eff_max ≠ ∞: verify n ≤ eff_max.
On failure
- category
structural- path
<instance>/values- production
TemplateInstance- message
"NestedTemplateInstance count above maximum (got <n>, expected ≤ <eff_max>) for key '<embeddedTemplate.key>'"
- Verify n ≥ eff_min.
- If req = “recommended” or req = “optional”:
- If n > 0:
- Verify n ≥ eff_min.
On failure
- category
structural- path
<instance>/values- production
TemplateInstance- message
"NestedTemplateInstance count below minimum (got <n>, expected ≥ <eff_min>) for key '<embeddedTemplate.key>'"
- If eff_max ≠ ∞: verify n ≤ eff_max.
On failure
- category
structural- path
<instance>/values- production
TemplateInstance- message
"NestedTemplateInstance count above maximum (got <n>, expected ≤ <eff_max>) for key '<embeddedTemplate.key>'"
- Verify n ≥ eff_min.
- If n > 0:
Out of Scope
The following checks are outside the scope of the canonical algorithm and are not required for conformance:
ControlledTermSourcemembership — verifying that aControlledTermValue’sTermIriis drawn from a declared ontology, branch, class set, or value set requires an external ontology resolver and is not defined here.- NIH Grant ID pattern — the lexical pattern for
NihGrantIriis currently unspecified. AttributeValueFieldname validation — attribute names are not fixed at schema definition time and cannot be structurally validated against the schema.
Open Questions
- Which validation rules should be mandatory in the core specification versus deferred to profile-specific extensions?