Skip to content

Write a Contract

An Invaract contract is a YAML document describing a transformation’s inputs and outputs — shaped after the Open Data Contract Standard (ODCS), not a new format invented from scratch. This guide builds one up using the real contract this repository ships, demo/contracts/invaract_output.yaml.

id: invaract_demo_output
version: "1.0.0"
status: active
  • id — a stable identifier for this contract.
  • version — MAJOR.MINOR[.PATCH], as a string ("1.0.0" or "1.0").
  • status — free-form; defaults to active if omitted.

Each input is a dataset the transformation reads:

inputs:
- name: orders
location: demo/input/sample.csv
format: csv
schema:
fields:
- name: id
type: integer
required: true
nullable: true
- name: value
type: integer
required: true
nullable: true
  • name — logical name, used for lookups and violation messages.
  • location — the physical location Invaract matches against the real Spark plan (a table name or a file path). Spark reports absolute file: URIs at runtime; a relative location declared here still matches, by suffix — see Contract Format.
  • format — optional, e.g. csv, parquet, delta.
  • schema.fields — the fields, each with a name and type. A dataset whose content no contract governs (a folder of rejected text lines, say) can leave schema out: its columns are then not checked, and validation warns so the omission is never an accident.

At least one output is required — this is what a contract actually gates:

outputs:
- name: result
location: demo/output/result.parquet
format: parquet
saveMode: overwrite
schema:
fields:
- name: id
type: integer
required: true
nullable: true
- name: value
type: integer
required: true
nullable: true
- name: value_squared
type: integer
required: true
nullable: true
- name: value_tier
type: string
required: true
nullable: true

saveMode (append/overwrite/ignore/error) is checked against the write’s actual save mode when both are known. A dataset can also declare catalog: { required: true, ... } to require it be registered in a real data catalog, not just written to a bare path — see Require Catalog Registration. It can also declare type: DATA_ASSET/SOURCE/CONTROL to say what it represents — a shareable data product, an external source, or pipeline control data — see Declare Input and Output Types.

If the contract declares more than one output, an output can also say which of the contract’s inputs it is built from with derivedFrom: [input_name, ...]. Without it, every write is expected to read every declared input — see Map Outputs to Their Inputs.

KeyDefaultMeaning
requiredfalseThe field must be present in the actual schema.
nullable!requiredThe field’s values may be null.
properties—Nested fields, for a struct type.
sensitivityTagsnoneGovernance labels (e.g. [pii, financial]) — see Reference → Contract Format’s “Sensitivity tags”. Declared on an input field, propagated to every output column derived from it — reporting only, not enforced.
constraintsnoneStatic data-quality requirements (equals/oneOf/range) — see Verify Static Data Quality. Unlike sensitivityTags, this is enforced (opt-in, via staticDataQuality) — but only when the transformation’s own logic provably guarantees a violation, never merely predicts one.

rules is a list of {type, ...properties} entries. Most rule types are recorded for future use but not yet enforced; seven are interpreted and enforced today, across two families — three for row-level MERGE/UPDATE/DELETE statements, four for a transformation’s overall shape:

rules:
- type: merge_condition
columns: [customer_id, region]
- type: forbid_unconditional_delete
- type: allowed_update_columns
columns: [status, updated_at]
- type: required_group_by
columns: [customer_id]
- type: forbid_cross_join

See Enforce Row-Level DML Rules and Enforce Transformation Shape Rules for what each one checks.

Any top-level key Invaract doesn’t recognize — and the explicit extensions map — are preserved verbatim, not rejected:

extensions:
owner: data-platform-team

This keeps a contract additive to the wider ODCS ecosystem: fields your organization cares about (owners, SLAs, quality rules) round-trip through Invaract even though it doesn’t act on them yet.

Point your editor at the contract’s JSON Schema for inline validation and autocomplete. With the YAML Language Server (the VS Code YAML extension, among others), put this comment at the top of the contract:

# yaml-language-server: $schema=https://mlltx.github.io/Invaract/schemas/contract/invaract-contract.schema.json
id: my_contract
# ...

The schema is published at https://mlltx.github.io/Invaract/schemas/contract/invaract-contract.schema.json, from the same file the contract parser is tested against. It checks a contract’s shape (the required keys, the field types); the parser and validator still decide the rest, such as duplicate names. Inside a clone of this repository, the contracts under demo/contracts/ use the relative path ../../contract/schema/invaract-contract.schema.json instead.

For every field, its type, and the complete set of validator checks, see Reference → Contract Format.