For dbt and Snowflake teams

Know what your SQL does before the warehouse runs it.

Semiring checks dbt models as you write them: join fan-out, NULLs, column lineage, broken downstream columns. Then it runs them on a local Snowflake and reviews every pull request for what the change does to your data. Built for people, and for the agents writing SQL beside them.

In development. dbt + Snowflake first.

One loop, from first keystroke to merged pull request

Concept demo

Static checks

A type checker for what a query means.

Semiring reads the compiled SQL of every dbt model and works out what each column is made of, which rows survive, and how many times each one can repeat. It answers from the code. Nothing is executed and no data leaves your warehouse.

I want this first
orders
order_id
1042
×
payments
order_idamount
104280
104240
×
shipments
order_idid
1042S1
1042S2
=
joined
amountid
80S1
80S2
40S1
40S2
1 ⊗ 2 ⊗ 2 = 4 rows. sum(amount) returns 240 for an order worth 120. Joins multiply; Semiring does the multiplication before you run it.
Fan-out
Finds joins that can repeat rows, and the aggregates they inflate. Proven impossible when a key guarantees one match.
Nulls
Tracks NULL through outer joins, coalesce, case without else and empty aggregates, then checks it against your not_null tests.
Lineage
Column-level, through CTEs, windows, unions and flatten. Each path is graded proven, approximate or not traceable, so you know how far to trust it.
Keys and grain
Derives what makes a row unique from the SQL, and tells you which unique and not_null tests it can already back.
Incremental
Says what an incremental model needs to stay correct: its cursor, its lookback, the rows it must reread.
Drift
Compares the columns your code publishes with what the warehouse catalog says is deployed.

Semantic review in CI

A reviewer that reads the algebra, not the diff.

A forty-line SQL diff can be a harmless refactor or a silent change to revenue. Semiring compares the meaning of both versions, tells you which columns and rows can differ, and looks for a concrete row that proves it.

I want this first

Moved a subquery to a CTE

− from (select * from stg_payments
−       where paid) p
+ from paid p

equivalentPublished columns are identical. Nothing downstream can change.

Tightened a join

− left join stg_shipments s
+ inner join stg_shipments s

changedChanges which rows. Order 1042 has no shipment: present in base, missing in head.

Dropped a column

−     o.customer_id,

brokendim_customers reads fct_orders.customer_id, which is no longer published.

Verdicts
Every changed model is equivalent, changed or broken, with the SQL fragment responsible and the evidence behind the call.
Counterexamples
For a changed model, Semiring synthesizes a small input, runs both versions locally and shows the rows that differ. Duplicates and NULLs included.
Blast radius
Lists the downstream columns a change can reach, and whether it touches their values, their rows, or both.
Smaller CI
Build and test the models a change can reach. Skip the hundreds it cannot.
Honest limits
When SQL is outside what it can analyze, the report says so and widens the answer. It never guesses quietly.

Local Snowflake

Snowflake, on localhost.

A Snowflake-compatible endpoint backed by DuckDB. Your connector, your dbt profile and your Snowflake SQL, pointed at 127.0.0.1. The edit-run loop stops waiting on a warehouse, and CI stops paying for one to check that a model compiles and its tests pass.

I want this first
Same drivers
The Snowflake Python connector, the Go driver, dbt-snowflake and the SQL REST API connect unchanged.
Snowflake SQL
QUALIFY, FLATTEN, VARIANT paths, IFF, LISTAGG, MERGE, stages and COPY INTO. Snowflake's dialect, including the parts DuckDB does not speak.
Fails closed
SQL it cannot run faithfully is rejected with a reason. A local pass should mean something.
Cheaper CI
Run dbt builds and tests on the CI runner. Keep the warehouse for the data that needs it.

For coding agents

Give your agent something to check its SQL against.

Agents write SQL that looks right. Without a compiler that understands joins and NULLs, the first thing to tell them otherwise is production. Semiring gives them the same answers it gives you, as tool calls, with no warehouse credentials.

I want this first
One call, one answer
explain, impact, criteria, diff: each a CLI command with a JSON form, usable from Claude Code, Codex or Cursor.
Graded evidence
Every answer says how sure it is, so an agent knows what it has established and what it still has to test.
Tests that matter
For any model, the input cases a test has to cover: match, no match, NULL key, multiple matches.
A safe place to run
The agent verifies its change on the local endpoint. It never needs a production role.

Desktop IDE

All of it, in one window.

A desktop editor for dbt projects where the checks, the lineage, the local warehouse and the agent share one index of your project. The demo at the top of this page is the idea.

I want this first
Inline findings
Fan-out, NULL and broken-reference findings on the line that causes them, updated as you type.
Hover lineage
Point at a column to see where its value comes from and what reads it, across models.
Run locally
Build a model and its children on the bundled endpoint. Results in the editor, in seconds.
Review before push
See the semantic review your pull request will get, before you open it.

From the prototype

96.2%of models analyzed exactly on a 1,613-model dbt project
10 sto re-analyze that project after editing a model
2.6 sto build Jaffle Shop, 13 models and 30 tests, on the local endpoint
0rows read by the static checks. They work from compiled SQL.

Early access

Tell us what to build first.

Semiring is in development and none of it is generally available yet. Leave an email and pick what you would use. The answers decide the order we ship in, and we will write when there is something for you to try.

You are on the list.

We will email you when there is something to try. Nothing else.

What would you use first?

We use your email to tell you about Semiring early access, and for nothing else.

The name

Union adds. Join multiplies.

A semiring is a set with two operations, ⊕ and ⊗. Relational queries have that shape: a union adds rows and a join multiplies them. Read SQL as algebra and questions like “can this join repeat a row?” or “can this column be NULL?” stop being matters of opinion. They can be computed.

The mark is the two operators fused into one star, under half a ring.