# Data contract

A data contract is the spec's last mile: the document a consumer can hold a
producer to, and the document an AI agent can build a pipeline from without
asking anyone anything. It is short by design. Everything in it is either a
fact about the data or a rule that can be checked by a machine.

Fill every section. A blank section is a decision the consumer will make for
you, later, at the worst time.

---

```
# Data contract: <dataset name>

Version: <major.minor.patch>          Status: <draft | active | deprecated>
Producer: <team or system>            Consumer(s): <teams or systems>
Owner: <one named role, not a group>  Escalation: <where a breach is reported>

## Purpose

<Two sentences. What decision or process the consumer runs on this data,
and what goes wrong for them if it is late or wrong. This is the sentence
that settles every later argument about priority.>

## Grain and keys

One row is: <the thing a single row represents, e.g. one instrument per
business date>
Primary key: <field(s)>
Uniqueness: <guaranteed | not guaranteed, and why>

## Schema

| Field | Type | Nullable | Source | Rule |
| --- | --- | --- | --- | --- |
| <name> | <type, with precision or format> | <yes/no> | <system.table.column or derivation> | <the validation a machine can run> |
| <name> | <type> | <yes/no> | <source> | <rule> |

Rules are checkable statements: "between 0 and 1", "matches ^[A-Z0-9]{9}$",
"present when status = active", "equals the sum of X and Y". Not "should be
valid."

## Data-quality rules

| Dimension | Rule | Threshold | Checked how |
| --- | --- | --- | --- |
| Completeness | <field> populated for <population> | <e.g. 99.5 percent of rows> | <query or job name> |
| Uniqueness | No duplicate <key> per <grain> | 100 percent | <query> |
| Freshness | Latest <date field> within <N> of <cutoff> | <N> | <query> |
| Validity | <field> within <range or set> | 100 percent | <query> |
| Consistency | <field> agrees with <reference> | <threshold> | <query> |

## Availability

Delivery: <where the data lands and in what form>
Cutoff: <the time by which each period's data is complete, with time zone>
Target: <e.g. available by cutoff on 99 percent of business days per month>
Measured by: <the job or dashboard that computes the number, not a person>
Late-data policy: <what the consumer should assume before the cutoff, and
how a late delivery is announced>

## Change policy

Breaking (major): removing or renaming a field, changing a type, tightening
a nullable to not-null, changing the grain.
Additive (minor): new optional field, new allowed value, looser threshold.
Fix (patch): documentation, a correction that changes no shape or rule.

Breaking changes require: <notice period>, <a parallel-run window>, and
consumer sign-off recorded in <where>.

## Examples

Provide at least one row of each. These double as test fixtures.

Happy path:
| <field> | <field> | <field> |
| <value> | <value> | <value> |

Edge case (<what makes it an edge>):
| <value> | <value> | <value> |

Error case (<what the consumer should see, and what they should do>):
| <value> | <value> | <value> |

## Open questions

<None, ideally. If there are any, the contract is a draft, not active.>
```

---

## Why each section exists

| Section | The failure it prevents |
| --- | --- |
| Purpose | A dispute about priority with no shared fact to settle it |
| Grain and keys | Two teams joining on different ideas of "one row" |
| Schema with rules | A field that is "valid" in the producer's head and invalid in the consumer's job |
| Data-quality rules | Quality as a feeling, argued about after the fact |
| Availability | Late data discovered by the consumer's users |
| Change policy | A rename shipped on a Friday that breaks a consumer on a Monday |
| Examples | A pipeline built to the schema that fails on the first real row |

---

## Two rules for using it

1. **Every field traces to a source.** The Source column is not
   documentation; it is evidence. If you cannot name where a field comes
   from, you are not ready to promise it.
2. **Every rule is a query.** If a rule cannot be run, it is a hope. Write
   the query, run it against real data before the contract goes active, and
   record what it returned.
