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

Run a contract-approval review

Before an attribute is trusted enough to bind into a contract, a human has to have looked at it and said so. Kirimana models that as a review-state machine on every catalog column: the attribute moves through a small, closed set of states, each transition is recorded with an actor and a reason, and discovery is never allowed to promote an attribute on its own. This page covers the state machine, the verbs that drive it, how many approvals a change needs, and the audit trail it leaves.

The attribute review-state machine

Every catalog column carries a review_state, orthogonal to the governing contract’s medallion state. Medallion answers “which data-platform layer is this in?”; review state answers “has a human approved this attribute for contract binding?”. The states and the allowed transitions between them are a closed menu — no other move is possible:

(discovery) ──► new ──► in_progress ──► submitted ──┬──► approved

                                                     └──► rejected
             approved ──► in_progress   (governance re-edit)
             rejected ──► in_progress   (steward re-opens)
  • new — where discovery lands a column. Discovery stamps provenance and nothing more.
  • in_progress — a steward is editing metadata (classification, description, PII flags).
  • submitted — the review is open, like a PR waiting on review.
  • approved — a human has signed off; the column may bind into a contract.
  • rejected — a reviewer declined it, with a required reason.

The critical invariant: new → submitted and new → approved are forbidden. Discovery never auto-promotes an attribute. A machine can populate the queue; only a human moves the needle. And both approved and rejected are re-openable back to in_progress — governance can re-edit an approved attribute, and a steward can re-open a rejected one — so a decision is never a dead end, but every re-open is itself a recorded transition.

The verbs

kiri attr drives the machine. A column is referenced as <asset_urn>#<column_name>:

# Triage the queue — what did the last sync discover?
kiri attr list --state new --domain sales

# Edit metadata while in_progress (moves new → in_progress).
kiri attr edit sales.orders#customer_email \
  --classification confidential \
  --description "Customer email, PII"

# Open the review (in_progress → submitted).
kiri attr submit sales.orders#customer_email

# Sign off (submitted → approved).
kiri attr approve sales.orders#customer_email

# ...or decline, with a required reason (submitted → rejected).
kiri attr reject sales.orders#customer_email \
  --reason "email belongs on the contact table, not orders"

# Re-open either terminal state back to in_progress.
kiri attr reopen sales.orders#customer_email

Every verb takes --actor to override the identity recorded in the history (it defaults to the environment). kiri attr show and kiri attr history print one column with its full review history. The same write path backs the web app’s Catalog page, so a review done in the UI and one done at the CLI land identical transitions and history entries — there is one review engine, two surfaces.

kiri attr reject requires --reason, and the requirement is not cosmetic: a rejection with no stated reason is forensically useless. The machine refuses the transition without one.

Approval counts scale with sensitivity

How many approvals a contract change needs is resolved separately, at PR time, and it rises with what the change does. kiri contract approval-count computes the required number from the diff: a base count per touched domain, more when the change raises a classification to restricted, and more again when it introduces a new cross-domain consumer. A routine edit inside one domain needs the domain’s base count; a change that raises restricted or crosses a domain boundary automatically needs elevated sign-off. See PR-time governance gates for wiring this into CI.

The number of humans who must approve, and the roles allowed to approve at all, come from RBAC: the approve and promote_gold capabilities are approver-and-above, and promotion to gold is scoped to the owning domain.

A separate flow: gated AI queries

Do not confuse attribute review with the AI-query approval flow, which is a different surface with its own verbs. When the AI query gate decides a query needs a human before it runs, it opens an approval request; operators vote on it with kiri approve:

kiri approve list --workspace ws-prod
kiri approve show <request-id>
kiri approve grant  <request-id> --as alice@acme.com
kiri approve reject <request-id> --as bob@acme.com --rationale "too broad"

Here a single reject vote blocks the request (fail-fast — one objection is enough), and the required approve count is snapshotted at issue time so a later policy change does not retro-affect an open request. This flow governs query execution, not attribute review — keep the two mental models distinct. The rest of this page is about attribute review.

The audit trail

Every successful attribute transition appends an immutable entry to the column’s review_history: the from-state, the to-state, the actor, the timestamp, and the reason. That tuple is the forensic record — who moved this column from which state to which, when, and why. Because re-opens are transitions too, an attribute that was approved, re-edited, and re-approved carries every step, not just the latest verdict.

# The full decision history for one column.
kiri attr history sales.orders#customer_email

This history is what an auditor reads to answer “prove this sensitive column was reviewed by a human before it went into a governed contract”. The closed-menu transitions guarantee the story is coherent — there is no way to reach approved except through submitted, and no way to reach submitted except through a human edit — and the append-only history guarantees the story is complete.

Putting it together

The review loop is short and hard to cheat. Discovery fills the queue with new columns. A steward edits and submits. An approver signs off or rejects with a reason. The contract change carrying the result goes through a PR whose required approval count scales with its sensitivity. And every step, including every re-open, is recorded on the column itself. Nothing reaches a governed contract without a named human having approved it, and nothing that human did is invisible after the fact.

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