Handle schema drift
The contract is the source of truth; the warehouse table is a materialisation of it. Drift is what you call it when the two disagree — someone altered a column in the catalog, or a source changed shape under a bronze table, or a proposed contract edit would break a consumer. Kirimana catches drift in two places: at apply time against the live table, and at PR time against the previous version of the contract. Editing the catalog directly is drift; Kirimana surfaces it rather than quietly pulling it back.
Apply-time drift: the live table diverged
When a table already exists in the catalog, you want to know whether it
still matches its contract before you trust it. kiri contract verify-live diffs the live Unity Catalog table against the contract
and reports every difference:
kiri contract verify-live \
--contract contracts/dim_customer.yml \
--catalog acme_prod \
--schema gold \
--fail-on type-mismatch
--fail-on is a closed menu — add, drop, type-mismatch,
meta-mismatch, or all — and it decides which class of finding fails
the command. A column added to the live table that the contract doesn’t
know about is an add; a contract column missing from the live table
is a drop; a column whose live type no longer matches the contract is
a type-mismatch. The default --fail-on type-mismatch blocks on the
most dangerous class (wrong types silently corrupt reads) while
reporting the rest. The JSON report lands at verify-live.json unless
you redirect it. Wire this into CI against your production catalog and a
schema someone hand-edited can’t stay hidden.
When the live table is the truth you want to adopt
Sometimes drift is intentional — a source legitimately gained a column
and you want the contract to catch up. kiri contract sync-from-live
reads the live table and proposes the patch that would bring the
contract back in line:
kiri contract sync-from-live \
--contract contracts/dim_customer.yml \
--catalog acme_prod \
--schema gold \
--propose
--propose writes a reviewable YAML proposal (./sync-proposal.yml by
default) — it does not touch the contract. You read the proposal, and
only if you agree do you re-run with --apply to patch the contract on
disk. The two are mutually exclusive, and --apply is refused when
project policy is fail_closed, so adopting live schema is always a
deliberate, reviewed act. The reconciliation flows one way: change the
YAML, open a PR, apply — never the reverse.
Full-load drift: the source changed shape
For a full-load bronze table, drift originates upstream — the source
system changed its columns between loads. kiri sources discover can
compare a discovered stream against a contract’s declared columns and
print a drift report before you ever apply:
kiri sources discover --against-contract silver_customer
This tells you a source gained, lost, or retyped a column while the contract still expects the old shape — the earliest possible warning, at the boundary where the data enters, so you fix the contract before a full load lands data that violates it.
PR-time drift: a proposed contract change
The second kind of drift is a change you’re about to make. Before a
contract edit merges, you want a semantic-aware diff of what actually
changed — not a raw text diff that flags reordered keys, but one that
understands the contract model. kiri diff compares two contract
files:
kiri diff contracts/dim_customer.yml /tmp/dim_customer.proposed.yml
For silver contracts specifically, kiri silver diff A B gives the
same semantic comparison scoped to silver shapes. And kiri contract lint runs the whole governance rule set across the changed project,
which is where breaking-change and compatibility findings surface at
review.
What blocks and what warns
Kirimana is deliberate about severity — not every difference should stop a release, but the dangerous ones must:
- Blocks (error / non-zero exit): a
type-mismatchunder the defaultverify-livepolicy; any error-severitykiri contract lintfinding; a validation failure fromkiri contract validate --strict. These fail CI and keep the change off main. - Warns (reported, exit 0): a live column addition or drop under the
default
verify-livepolicy (surfaced, not fatal unless you widen--fail-on); deprecation findings; anything below error severity.
The rule of thumb: silent divergence is never allowed — every drift is
reported — but you choose, per class, which ones are hard gates. Widen
--fail-on all when you want zero tolerance, keep the default when you
want types guarded and additions merely visible.
Post-migration reconciliation
When you migrate a table from a legacy warehouse onto your platform,
schema agreement isn’t enough — you also want to know the data
reconciles. kiri reconcile runs post-migration validation between the
source and the target: row counts, primary-key coverage, null rates,
date bounds, and SCD2 business-key alignment, emitting a JSON or
markdown report. It answers the question verify-live can’t: not just
“does the shape match” but “did every row arrive intact.” A locked
report mode omits raw values when the reconciliation itself must stay
governed.
Where next
- Write your first data contract — the contract that is the truth drift is measured against.
- Deprecate a contract — the lifecycle move for when a contract should go away rather than be reconciled.