Skip to content

Cross-team contracts

When one team’s models read another team’s tables, a column the producer drops or narrows breaks the consumer’s pipeline — usually discovered at run time, in production, far from the change that caused it. Cross-team contracts move that failure to compile time on the consumer’s side: rocky compile fails when an upstream project changes a column the downstream project actually reads.

The two projects stay fully independent. There is no shared repository, no shared state, and no runtime coupling — only a vendored snapshot file and a diff.

  1. The producer publishes a snapshot. rocky publish-ir compiles the producer project and writes its typed ProjectIr — every model’s resolved columns and types — to a JSON file.

    Terminal window
    rocky publish-ir --with-seed --out project-ir.json

    Pass --with-seed for a self-contained DuckDB producer so leaf models resolve concrete column types. Without resolved types the contract has nothing to check, so publish-ir refuses to write a snapshot whose models are all empty rather than ship one that looks enforced but checks nothing.

  2. The consumer vendors it and declares an [imports.<name>] block:

    [imports.orders]
    path = "vendor/orders" # directory holding the vendored snapshots
    snapshot = "current.json" # the producer's current published snapshot
    baseline = "baseline.json" # the reviewed-and-accepted "before" image
    pin = "*" # optional recipe-hash pin ("*" = trust any)
  3. The consumer’s rocky compile checks the contract. It links each consumer model to the producer table it reads (via the model’s [[sources]] entry), diffs the baseline against the current snapshot, and emits a diagnostic for any change that touches a column the consumer actually reads.

CodeProducer changeSeverityFires when the consumer…
E030column droppederrorreferences the column
E031column type narrowederrorreferences the column
E032column went nullable → NOT NULLerrorreferences the column
W031column type widenedwarningreferences the column
W030column addedinforeads the producer via SELECT *
E033snapshot drifted from a concrete pinerroralways (whole-project tripwire)
E034snapshot is a newer format than this builderroralways (fail closed)

A consumer that selects explicit columns is unaffected by changes to columns it does not read. A consumer that uses SELECT * can’t enumerate its columns, so Rocky falls back to flagging every relevant change — over-reporting rather than letting a breaking change slip through.

pin and baseline answer different questions

Section titled “pin and baseline answer different questions”

They are complementary, not redundant:

  • baseline is the column-level before image. It is the only input that lets the diff emit the column codes (E030E032, W030/W031) for the columns the consumer reads.
  • pin is a whole-project drift tripwire. Set it to a concrete recipe hash and rocky compile fails (E033) if the vendored snapshot differs at all — even for changes that touch no column the consumer reads. Leave it at "*" (the recommended default) to fail only on changes to your reads.

Nothing advances the baseline automatically — that is deliberate. The baseline is the schema you have reviewed and accepted. When the producer ships a change and you’ve decided to take it, advance the baseline with:

Terminal window
rocky imports update # advance every import's baseline to its snapshot
rocky imports update --check # CI guard: fail if any baseline is behind or pin is stale

--check writes nothing and exits non-zero when an import is out of date — drop it in CI to ensure vendored contracts stay in sync.

Rocky reads a file at the configured path; how it gets there is up to you. The simplest durable transport is a git submodule: add the producer’s repository (or a small artifact repo it publishes to) as a submodule under path, and the producer commits its publish-ir output there. The consumer then runs:

Terminal window
git submodule update --remote # pull the producer's latest snapshot
rocky imports update # advance the baseline once you've reviewed it

The snapshot carries a snapshot_version header, so a consumer on an older build fails closed (E034) against a newer format rather than silently mis-reading it.

See also the governance guide for how contracts fit alongside Rocky’s other trust controls.