Skip to main content
Private Preview·Early access by invitation.Request access →
Kirimana.
Docs · Contracts

Deprecate a contract with the state machine

A contract doesn’t just get deleted. Other contracts read from it, lineage points at it, and a catalog table backs it — so removing it is a governed lifecycle move, not a rm. Kirimana models that lifecycle as a state machine with a closed menu of transitions, so “this dataset is going away” becomes an explicit, auditable state that downstream consumers can see and plan against.

The canonical lifecycle state

Every contract carries a single canonical lifecycle state, kiri.lifecycle.state, whose value is one of a fixed set:

draft → bronze → silver → gold → deprecated

That is the promotion journey: a contract is drafted, promoted through the medallion layers as it earns trust, and eventually deprecated when it should no longer be used. deprecated is a first-class state on the same axis, not a separate flag.

kiri.lifecycle.state is the canonical home for state. An older kiri.medallion.state path still parses but is a deprecated alias that resolves to it — new contracts author kiri.lifecycle.state, and kiri contract lint flags the alias.

Two things are worth keeping distinct from lifecycle state:

  • Medallion layer (kiri.medallion.layer) is the destination — which layer the contract materialises to. A deprecated gold contract is lifecycle.state = deprecated, medallion.layer = gold: the table still exists in gold, but no new silver→gold transformation runs for it.
  • Active (kiri.medallion.active) is orthogonal. A contract can be in silver state but active: false — paused from apply runs without being demoted. Pausing execution and deprecating a contract are different acts.

Transitioning states

You move a contract along the lifecycle with a single command rather than hand-editing the state field:

kiri contract promote dim_customer --to deprecated

--to is a closed menudraft, bronze, silver, gold, or deprecated. Nothing else is accepted, so a typo or an invented state is rejected at the boundary rather than written into a contract. To pause a contract’s execution while deprecating it, flip active in the same move:

kiri contract promote dim_customer --to deprecated --inactive

--inactive pauses runtime execution; --active resumes it; omit both to leave activation unchanged. Lifecycle transitions themselves are validated — kiri.lifecycle.phase transitions follow a state machine (proposed → beta → active → deprecated → sunset) with no backwards jumps, and the semantic validator enforces it at kiri contract validate, kiri plan, kiri apply, and in CI.

Retirement and the ODCS status gate

Underneath the finer lifecycle metadata, ODCS status is the coarse gate: draft / active / deprecated / retired. retired is the end of the line — the contract is not merely discouraged, it is gone. Kirimana ties one rule to it: a contract at ODCS status = retired must declare a kiri.lifecycle.migration_target — the contract ID consumers should move to — or an explicit none. You cannot retire a contract and leave its consumers with nowhere to go without saying so on the record.

Supporting metadata rounds out a graceful sunset:

customProperties:
  - property: kiri.lifecycle.state
    value: deprecated
  - property: kiri.lifecycle.sunset_date
    value: 2026-12-31
  - property: kiri.lifecycle.migration_target
    value: io.acme.sales.gold.dim_customer_v2

sunset_date is the date the contract will be removed; migration_target is where to go instead. Both travel with the contract, so a consumer reading it sees the deprecation, the deadline, and the replacement in one document.

The review-state machine

Lifecycle state is per-contract. There is a second, distinct state machine that is per-attribute — per column, per metric, per reporting goal — tracking the governance review of that specific attribute: kiri.attribute.review_state, whose canonical values are new, in_progress, submitted, approved, and rejected. It answers a different question than lifecycle: not “where is this contract in its promotion journey” but “has a human approved this individual attribute for contract binding.” The two are deliberately not interchangeable — an approved attribute can live on a contract that is itself deprecated.

The review flow moves an attribute from discovery to binding through a closed set of transitions:

(discovery) ──► new ──► in_progress ──► submitted ──┬──► approved
                                                    └──► rejected
             approved ──► in_progress   (governance re-edit)
             rejected ──► in_progress   (steward re-opens)

A newly discovered attribute starts at new; a steward takes it to in_progress, submitted opens the review, and a reviewer lands it at approved or rejected. new → submitted and new → approved are forbidden — discovery never self-approves. Both terminal states can reopen to in_progress (a governance re-edit or a steward re-opening a rejection), so nothing is deleted and the next schema-discovery run doesn’t re-queue a decision the organisation already made. Every menu of transitions is closed: an attribute can’t jump to a state the machine doesn’t allow from where it is.

What deprecation does downstream

Marking a contract deprecated isn’t cosmetic — it changes what the rest of the platform reports about it:

  • Consumers. Lineage records which contracts depend on this one. kiri.lineage.downstream lists them (inferred from others’ declared upstream), and kiri.cost.consumer_allowlist bounds who may read it. When a contract deprecates, every downstream contract is by definition depending on something on its way out — surfaced so owners can migrate to the migration_target before the sunset_date.
  • Lineage. The contract stays in the graph while deprecated — its edges don’t vanish — so you can still trace what fed it and what it fed. Deprecation is a visible node state, which is the point: a consumer walking lineage sees the deprecation rather than discovering a broken dependency at apply time.
  • Catalog. The physical table persists. A deprecated gold contract’s table still exists in the catalog; what stops is new transformation runs promoting fresh data into it. When you push contract metadata to Unity Catalog, the lifecycle state and deprecation annotations project onto the catalog object, so a consumer browsing the catalog — not just the contract repo — sees the same “deprecated, migrate to X by Y” signal.

Where next

Updated 5 July 2026 · v1.0.0-beta.1