CLI Reference¶
akad infer --name NAME [--format parquet|sql] [--location PATH | --connection-string URL --table-name NAME] [--output PATH]
akad diff --old PATH --new PATH | --name NAME --old-version V --new-version V --registry-url URL [--output text|json]
akad check --contract PATH
akad publish --contract PATH --registry-url URL
akad validate --contract PATH [--registry-url URL] [--output text|json]
akad list --registry-url URL
akad history --name NAME --registry-url URL [--limit N]
| Command | Purpose | CI-friendly |
|---|---|---|
akad infer |
Profile an existing dataset and scaffold a starter contract YAML | — |
akad diff |
Compare two contract versions; flag breaking vs non-breaking changes | Yes — fail the build on a breaking contract change |
akad check |
Parse and validate contract YAML syntax without touching data | Yes — catches typos before they hit a pipeline |
akad publish |
Register a contract version with the registry | — |
akad validate |
Run full validation against the dataset; exits 1 on breach |
Yes — fail the build on a breach |
akad list |
List all current contracts in the registry | — |
akad history |
Show recent validation runs for a contract | — |
akad infer — scaffold a starter contract¶
Profiles an existing dataset and writes a starter contract YAML — column types, nullability, low-cardinality allowed_values, key-like column quality rules, and a volume band around the observed row count.
akad infer --name daily_sales --location data/daily_sales.parquet \
--owner-team "Data Engineering" --owner-email data@example.com \
--output contracts/daily_sales.yaml
For a SQL dataset, use --format sql --connection-string ... --table-name ... instead of --location.
This is a starting point, not a finished contract — every inferred rule reflects only what the data looked like when profiled, not the rules it's actually supposed to follow:
allowed_valuesis only inferred for string columns where values repeat and stay under a cardinality cap — but it still only knows about values seen in the sample. A rare-but-valid value not present when you raninferwill show up as a breach later.- Volume bounds are a 0.5×–2× band around the observed row count — adjust to your pipeline's actual expected range.
on_breachalways defaults towarn— switch tofaildeliberately once you trust the contract.- Freshness rules are never inferred — there's no reliable signal for
max_age_hoursfrom a single snapshot.
Review and tighten the output before relying on it in CI or production.
akad diff — flag breaking changes before you publish¶
Compares two contract versions and classifies every change as breaking or non-breaking for a consumer relying on the old contract's guarantees.
# Two local files
akad diff --old contracts/daily_sales.yaml --new contracts/daily_sales.next.yaml
# Two versions already published to the registry
akad diff --name daily_sales --old-version 1.0.0 --new-version 1.1.0 --registry-url http://localhost:8000
Exits 1 if any breaking change is found — wire it into CI on the contracts repo to catch breaking changes before they're published, not after a consumer's pipeline breaks.
The rule applied throughout: loosening a guarantee is breaking, tightening one is not.
| Change | Breaking? | Why |
|---|---|---|
| Column removed | Breaking | A consumer reading that column fails |
| Column added | Non-breaking | Additive — nothing existing depends on it |
| Column type changed | Breaking | Consumer parsing/casting logic may fail |
nullable: false → true |
Breaking | Consumer assuming non-null may fail on a null |
nullable: true → false |
Non-breaking | Stronger guarantee, strictly compatible |
allowed_values gains a value |
Breaking | Exhaustive consumer handling (switch/case) may not cover it |
allowed_values loses a value only |
Non-breaking | Strictly fewer cases than before |
min_rows decreased, or removed |
Breaking | Weaker lower bound — consumer expecting at least N rows may not get them |
max_rows increased, or removed |
Breaking | Weaker upper bound — consumer with a fixed-size assumption may break |
max_age_hours increased, or removed |
Breaking | Data may now be staler than a consumer expects |
max_null_percentage / max_duplicate_percentage increased, or removed |
Breaking | Consumer assuming a stricter cap may break |
min_value decreased, or removed |
Breaking | Consumer assuming a stricter floor may break |
max_value increased, or removed |
Breaking | Consumer assuming a stricter ceiling may break |
| A quality rule removed entirely | Breaking | A guarantee is gone |
| A quality rule added | Non-breaking | A new guarantee, doesn't affect existing consumers |
| A business rule removed entirely | Breaking | A guarantee is gone |
| A business rule added | Non-breaking | A new guarantee, doesn't affect existing consumers |
| A business rule's expression changed | Breaking (always) | Strictness can't be inferred statically from arbitrary code — flagged conservatively for human review |
Out of scope by design: metadata (name, owner, tags), notifications, and consumer lists aren't compared — they don't affect what the data looks like to a consumer. on_breach and check_column changes are pipeline-internal, not surfaced either.