Skip to main content

Validation

Generated from the module documentation of crates/canon/src/validate/mod.rs.

Deterministic validation of a protocol/1 document.

Validation resolves every reference and reports every problem it finds, in a stable order that does not depend on the order the sections are written in:

  1. the format;
  2. malformed identifiers: the protocol id, then the ids declared in artifacts, evidence kinds, claims, obligations, actions, outcomes, invalidation rules, case inputs and independence dimensions, in that section order, then the capabilities and effect classes actions name and the protocol each action's case_open names, in action order, then the decisions outcomes require, in outcome order, then the values case inputs declare, in input order;
  3. duplicate identifiers, in the same section order;
  4. maximum ages that are not a whole number without leading zeros, followed by s, m, h or d, or that are but are too long to count in seconds (each with its own message), in the order the evidence kinds are written;
  5. case inputs that declare no value (empty-input-values, naming the input) or list a value more than once (duplicate-input-value, naming the input and the value, once for every listing after its first), in the order the inputs are written;
  6. action argument schemas outside the argument-schema dialect (action-arguments; the dialect, and the order within one schema, are stated with it), each naming the action, the JSON Pointer of the keyword inside the schema and why it is refused, one per offending keyword, in the order the actions are written;
  7. unresolved references and malformed input tests and order forms (a claim, an evidence kind, the artifact an evidence match names as its subject, which is reported after the kind it is written beside, then the evidence match's independent_on: an empty list, empty-independent-on, or a dimension it lists more than once, duplicate-independent-on, once for every listing after its first, then each dimension it lists that is not declared, undeclared-dimension, once, in the order listed, every one naming the declaration the match is written in; for an order form, each kind it names, first (or last) before then, then its subject, then a before whose first and then name one kind, self-ordered-evidence, naming it (an after or a since may name one kind as both: a record of a kind may follow the first, or the latest, record of that kind); for an input test, first an empty in list, empty-input-test, or a value it lists more than once, duplicate-input-test-value, once for every listing after its first, then an input it names that is not declared, undeclared-input, or a value it lists that its input does not declare, undeclared-input-value, each value once, in the order listed, every one naming the declaration the test is written in and the input; for a children form, first an empty in list, empty-child-outcomes, or an outcome it lists more than once, duplicate-child-outcome, once for every listing after its first, then open listed, open-child-outcome, once, then the evidence kind case_outcome when it is not declared, undeclared-evidence-kind, every one naming the declaration the form is written in): those in claims first, then those in obligations (each obligation's discharge predicate, followed by every evidence match, order form and children form it holds, which a discharge predicate may not: it tests only claim values, each kind an order form names once, the kind case_outcome for a children form; then its applicability predicate, followed likewise by every evidence match, order form and children form it holds, evidence-in-applicability: it tests only claim values and case inputs), then those in actions (each action's precondition, then each evidence kind it may produce, then the subject of the case it opens, undeclared-artifact, then an opening under the protocol's own id, self-opened-case, both naming the action), then those in outcomes, then those in invalidation rules (the upstream artifact, then each claim the rule invalidates); within a section, in the order its declarations are written, and within a declaration, in the order its references are written;
  8. invalidation rules that name a claim reaching no evidence match, in its own predicate or in a claim it tests through any number of claim references (inert-invalidation), in the order the rules and their claims are written. An order form or a children form is not an evidence match here: it reads the history, which an invalidation rule does not change;
  9. cycles between claims.

Within a section, entries come in the order they are written. Every message renders document text on one line. The same document always yields the same problems in the same order.

The argument-schema dialect​

Generated from the module documentation of crates/canon/src/validate/arguments.rs.

Canon's argument-schema dialect (story:action-arguments): the JSON Schemas an action's arguments may be.

The dialect is a subset of JSON Schema 2020-12. Every schema in it is a valid 2020-12 schema with the same meaning, so a runner can hand it to any 2020-12 validator; protocols load offline, so it has no reference at all.

  • Every schema, the top level and each nested one, names exactly one type: object, array, string, integer, number, boolean or null. The top level is type: object.
  • title and description, strings, anywhere.
  • enum, on any type: a non-empty list of distinct JSON scalars, each of the schema's type.
  • object: properties (a map of name to schema), required (a list of distinct names, each declared in properties), additionalProperties (a boolean only).
  • array: items (one schema), minItems, maxItems.
  • string: minLength, maxLength.
  • integer and number: minimum, maximum, integers.
  • minItems, maxItems, minLength and maxLength are non-negative integers, and each minimum is at most its maximum.
  • Schemas nest at most MAX_SCHEMA_DEPTH deep: the top level is one level, and each schema in properties or items one more.

Anything else is outside the dialect: $ref, $defs, $schema, $id, allOf, anyOf, oneOf, not, if, then, else, patternProperties, pattern, format, const, exclusiveMinimum and exclusiveMaximum by name, a list-valued type, a schema-valued additionalProperties, a keyword on a type it does not apply to and any unknown keyword.

check reports one problem per offending keyword, at the keyword's JSON Pointer inside the schema (an element's, for an enum value or a required name): a schema that is not an object, names no type or nests too deep is reported at its own pointer. A schema whose type is missing or not one of the seven has its other keywords checked only for being in the dialect, since which apply depends on the type. Problems come in document order, the keys of every object in code-point order, and the walk keeps a stack of its own, never recursing.

Problem codes​

CodeMeaning
unsupported-formatThe document's format is not protocol/1.
invalid-identifierAn identifier is empty or contains whitespace or a control character.
duplicate-identifierAn identifier is declared more than once in one section.
undeclared-claimA predicate tests, or an invalidation rule invalidates, a claim that is not declared.
self-ordered-evidenceA before order form names one evidence kind as both its first and its then: no record is observed strictly before itself.
undeclared-evidence-kindA predicate matches or orders, or an action may produce, an evidence kind that is not declared.
undeclared-artifactAn evidence match or an order form names as its subject, or an invalidation rule as its upstream artifact, an artifact that is not declared.
invalid-max-ageAn evidence kind's max_age is not a whole number without leading zeros, followed by s, m, h or d, or is too long to count in seconds.
evidence-in-dischargeA discharge predicate tests evidence; it may test only claim values. path locates the evidence match or order form inside the predicate, as discharged_when.all[1].evidence or discharged_when.first.
evidence-in-applicabilityAn applicability predicate tests evidence; it may test only claim values and case inputs. path locates the evidence match or order form inside the predicate, as applicable_when.not.evidence or applicable_when.before.
empty-input-valuesA case input declares no value, so no case could give it one.
duplicate-input-valueA case input lists a value more than once.
empty-input-testAn input test lists no value, so it is false in every case.
duplicate-input-test-valueAn input test lists a value more than once.
undeclared-inputAn input test names an input that is not declared.
undeclared-input-valueAn input test lists a value its input does not declare.
empty-independent-onAn evidence match demands independence on no dimension: independent_on is empty.
duplicate-independent-onAn evidence match lists a dimension in independent_on more than once.
undeclared-dimensionAn evidence match demands independence on a dimension that is not declared.
inert-invalidationAn invalidation rule names a claim that reaches no evidence match, in its own predicate or in a claim it tests, so no record could be kept from it.
empty-child-outcomesA children form lists no outcome, so no child case could end with one of them.
duplicate-child-outcomeA children form lists an outcome more than once.
open-child-outcomeA children form lists open, which is a running child case's outcome, never a terminal one: the form is unknown while a child is open.
self-opened-caseAn action opens a child case under the protocol's own id: a case could then open cases under its own protocol without end (D206).
claim-cycleClaims whose predicates test each other in a cycle; the first claim is repeated at the end.
action-argumentsAn action's argument schema is outside the argument-schema dialect: pointer is the JSON Pointer of the offending keyword inside the schema ("" for the schema itself) and reason why it is refused.

A document that is not well-formed YAML, or not shaped like protocol/1, fails earlier, while parsing; the command line reports it as parse.