Contract language#
Contracts are YAML or JSON documents. Run parcel schema for the JSON Schema, or add this line to YAML files for editors that support it:
# yaml-language-server: $schema=https://raw.githubusercontent.com/griot-cloud/parcel/main/schema/contract.schema.json
Contract fields#
The field definitions and examples are in Contract identity, Binding, Expose, and Rules.
The remaining sections specify expression syntax, supported types, and additional contract fields.
Rule constraints#
Rule IDs must be unique and match [a-z][a-z0-9_]*. assert and guarantee rules require on_fail. Any rule may have a message: text shown with it in reports. Conditions for decide, admit, assert, and guarantee must return a Boolean. A transform must return the type declared for its column in expose.
Shapes#
Operator |
Parameters |
Applies to |
|---|---|---|
|
Integer |
Groups with fewer than |
|
|
A stable sample chosen from the key. |
|
Positive |
Numeric exposed |
unless is an optional Boolean condition using ctx; when true, it skips the shape.
Namespaces#
Namespace |
Available fields |
|---|---|
|
Strings |
|
Source columns and declared |
|
|
Column statistics are <column>.null_count, null_rate, distinct_count, min and max. The application supplies ctx.now as the time used to evaluate rules. The application supplies ctx.other values.
Expressions#
parcel supports a checked subset of CEL. Unsupported syntax, types and operations are rejected during compilation.
Category |
Supported forms |
|---|---|
Values |
Boolean, integer, unsigned integer, double, string, bytes, timestamp, duration, lists and exact decimals. |
Operators |
Comparisons, arithmetic, |
Strings |
|
Conversions |
|
Timestamps |
|
Presence |
|
Lists |
|
A whole-number literal beside a double is read as a double, so row.cost > 0 works when cost is float64; beside an unsigned column it is read as unsigned. Only literals are converted: to compare an integer column with a double, convert it explicitly, as in double(row.quantity) * 1.5.
Exact decimals support up to 18 digits, no division, and one scale per comparison.
Built-in functions#
Function |
Result |
|---|---|
|
SHA-256 hash of a string. |
|
Fixed |
|
|
|
Checks the supported Kenyan mobile-number format. |
|
Checks email format. |
Rules may also call Custom functions loaded for the contract’s owner.
Nulls#
A null in a row field read by value makes an admit or assert false and a transform null. Use has(row.column) to test presence. A literal null is supported as a branch of a conditional, taking the other branch’s type:
ctx.clearance > 2 ? row.salary : null
Column types#
expose and extension declarations accept bool, int8 through int64, uint8 through uint64, float32, float64 (or double), utf8 (or string), large_utf8, binary (or bytes), timestamp, duration, decimal(p,s) and list<type>.
timestamp defaults to microseconds in UTC; explicit units use timestamp[s], [ms], [us] or [ns]. Duration units use the same bracket notation.
Additional fields#
Field |
Value |
|---|---|
|
Typed custom fields grouped under |
|
List of |
|
List of |
For example, this declares a calculated amount in major currency units:
extensions:
row: {amount_major: double}
enrich:
- {field: amount_major, expr: "double(row.amount) / 100.0"}
Rules can read the result as row.other.amount_major.
Inheritance#
A child contract names its parent with inherits. The CLI resolves parent contracts by name from YAML and JSON files in the same directory. A child can omit binding and expose to inherit them:
contract: purchasing/large_orders
version: 1.0.0
owner: acme
inherits: purchasing/orders
rules:
- id: large_orders
op: admit
expr: "row.amount >= 100"
The child’s rules accumulate with the parent’s rules. Here both the parent’s supplier filter and the new amount filter apply. A child may narrow expose to a subset of parent columns with the same types; it cannot expose a hidden parent column. A repeated binding must match the parent’s binding exactly, including its source form and partition columns. A child cannot remove or redefine a parent rule. Parent and child transforms compose in order, with the parent applied first.