BDD-style metric definitions with built-in data trust checks
Project description
BDD-style metric definitions with built-in data trust checks.
Install
pip install litmus-data
litmus init # scaffold litmus.yml + metrics/ folder
litmus check metrics/ # run trust checks
The problem
Your CEO asks: "What was our revenue last month?"
The data analyst says $4.2M. Finance says $3.8M. Engineering says $4.5M.
Three teams, three numbers, zero trust — because metric definitions live in SQL files, dbt models, and spreadsheet formulas that business users can't read or approve. Nobody agrees on what "revenue" means, and nobody knows if the underlying data is trustworthy.
The solution
Litmus lets you define metrics in plain English that business stakeholders can review and sign off on, then validates the data continuously. A .metric file is a Gherkin-inspired contract — Given / When / Then for the business logic, plus a Trust: block with executable data-quality rules.
Quickstart
Write metrics/revenue.metric:
Metric: Monthly Revenue
Description: Total revenue from completed orders in the current calendar month
Owner: finance-team
Tags: finance, reporting
Source: orders
Given all records from orders table
And status is "completed"
And order_date is within current calendar month
When we calculate
Then sum the amount column
The result is "Monthly Revenue"
Trust:
Freshness must be less than 6 hours
Null rate on amount must be less than 1%
Row count must not drop more than 20% day over day
Value must be between 100000 and 50000000
Value must not change more than 30% month over month
Run the checks:
$ litmus check metrics/revenue.metric
Monthly Revenue
Owner: finance-team
Trust Checks:
PASS Freshness: 2 hours (threshold: < 6 hours)
PASS Null rate on amount: 0.3% (threshold: < 1%)
WARN Row count: -18% day-over-day (threshold: < 20%)
PASS Value: $4,200,000 (range: $100,000 – $50,000,000)
PASS Change month-over-month: +12% (threshold: < 30%)
Trust Score: 4.5 / 5
Generate a stakeholder-friendly explanation:
litmus explain metrics/revenue.metric
What you get
| Command | What it does |
|---|---|
litmus init |
Scaffold litmus.yml and a starter metrics/example.metric. |
litmus check <path> |
Parse every .metric under <path> and run its trust rules against the warehouse. Exits non-zero on failure. |
litmus parse <file> |
Dump the parsed MetricSpec — useful when debugging DSL changes. |
litmus explain <file> |
Render a plain-English description from a spec (non-engineer friendly). |
litmus import-dbt <manifest.json> |
Seed .metric files from an existing dbt manifest.json. |
litmus export --to dbt <path> |
Emit dbt schema.yml plus singular tests from a .metric file, so Litmus rules run inside a dbt project. |
litmus share <path> |
Render a self-contained HTML card (with check results) you can paste into Slack or Notion. |
litmus report <dir> |
Produce an HTML, Markdown, or JSON report across a whole metrics folder. |
Run litmus <cmd> --help for the full flag list.
Trust checks
Nine built-in check types, all declarable inside the Trust: block:
- Freshness — maximum age of the most recent row (
Freshness must be less than 6 hours). - Null rate — share of nulls in a column (
Null rate on amount must be less than 1%). - Volume — row-count drop vs the previous period (
Row count must not drop more than 20% day over day). - Range — inclusive bounds on the metric's value (
Value must be between 100000 and 50000000). - Change — period-over-period movement on the value (
Value must not change more than 30% month over month). - Duplicate rate — uniqueness guard on a column (
Duplicate rate on invoice_id must be 0%). - Schema drift — fail if the source table's columns change (
Schema must not drift). - Distribution shift — flag when a column's distribution moves beyond a threshold (
Mean of net_amount must not change more than 25% month over month). - Custom SQL — pluggable hook in
litmus/checks/custom.pyfor rules the DSL doesn't express yet.
Change / distribution / volume rules compare against a SQLite history store (~/.litmus/history.db by default, overridable via LITMUS_HISTORY_DB or --history-db).
Supported warehouses
| Warehouse | Status | Install |
|---|---|---|
| DuckDB | Built-in, zero config | included |
| SQLite | Built-in | included |
| PostgreSQL | Supported | pip install 'litmus-data[postgres]' |
| Snowflake | Supported | pip install 'litmus-data[snowflake]' |
| BigQuery | Supported | pip install 'litmus-data[bigquery]' |
| All of the above | pip install 'litmus-data[all]' |
Warehouse credentials are read from the LITMUS_WAREHOUSE_USER and LITMUS_WAREHOUSE_PASSWORD environment variables — never from litmus.yml.
Run it on every PR
Drop this into .github/workflows/litmus-check.yml:
name: Litmus trust check
on:
pull_request:
paths: ["metrics/**/*.metric", "litmus.yml"]
permissions:
pull-requests: write
contents: read
jobs:
litmus:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- id: litmus
uses: zinnoberHaus/litmus@v0
with:
path: metrics/
- if: always() && github.event.pull_request
uses: marocchino/sticky-pull-request-comment@v2
with:
header: litmus
message: ${{ steps.litmus.outputs.summary-markdown }}
The composite action is defined in action.yml. Inputs: path (required), config, extras (e.g. postgres,snowflake), fail-on-warning, litmus-version. Outputs: report-json, trust-score, summary-markdown. An annotated copy of this workflow lives at .github/workflows/litmus-check.example.yml.
How it fits with dbt and semantic layers
Litmus is a trust / contract layer, not a replacement for your transformation or semantic tools. dbt and Cube / LookML / MetricFlow answer "how is this metric computed?" — Litmus answers "is the metric currently trustworthy, and did the business sign off on its definition?" litmus import-dbt seeds specs from an existing dbt project, and litmus export --to dbt emits dbt singular tests so the same rules can run inside a dbt CI pipeline. The two stacks are designed to sit side by side.
Documentation
- Getting Started
- Spec Language Reference
- JSON Report Schema
- Example specs — SaaS, e-commerce, and
examples/metrics/ - Changelog
- Contributing
- Code of Conduct
License
Apache 2.0 — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file litmus_data-0.1.2.tar.gz.
File metadata
- Download URL: litmus_data-0.1.2.tar.gz
- Upload date:
- Size: 67.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6efe7a21bb5d30d96b7ce68e297a5be21bdb710f9e797171a1700223f4304f4
|
|
| MD5 |
c0aaaee89a7f16cbf3394a17d60e39df
|
|
| BLAKE2b-256 |
df97073ed93fd03fcde2a350d1c5b83062e535247a1960ccd0a809120aa577bf
|
Provenance
The following attestation bundles were made for litmus_data-0.1.2.tar.gz:
Publisher:
release.yml on zinnoberHaus/litmus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
litmus_data-0.1.2.tar.gz -
Subject digest:
b6efe7a21bb5d30d96b7ce68e297a5be21bdb710f9e797171a1700223f4304f4 - Sigstore transparency entry: 1334655624
- Sigstore integration time:
-
Permalink:
zinnoberHaus/litmus@d40cff1ebf3d7a357a87c1dcf907acecc94b6e0b -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/zinnoberHaus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d40cff1ebf3d7a357a87c1dcf907acecc94b6e0b -
Trigger Event:
push
-
Statement type:
File details
Details for the file litmus_data-0.1.2-py3-none-any.whl.
File metadata
- Download URL: litmus_data-0.1.2-py3-none-any.whl
- Upload date:
- Size: 78.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
276f91546a23da66f3bb7654075dc4ef4a0c37fe7184562e8bc4b0c262cedfaa
|
|
| MD5 |
298ac6c25d37ea0ba48c908a7e0315df
|
|
| BLAKE2b-256 |
61325771bb14f2dac2a3b10ef333b1ea16399575bd269c58b240d3c5565f8275
|
Provenance
The following attestation bundles were made for litmus_data-0.1.2-py3-none-any.whl:
Publisher:
release.yml on zinnoberHaus/litmus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
litmus_data-0.1.2-py3-none-any.whl -
Subject digest:
276f91546a23da66f3bb7654075dc4ef4a0c37fe7184562e8bc4b0c262cedfaa - Sigstore transparency entry: 1334655789
- Sigstore integration time:
-
Permalink:
zinnoberHaus/litmus@d40cff1ebf3d7a357a87c1dcf907acecc94b6e0b -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/zinnoberHaus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d40cff1ebf3d7a357a87c1dcf907acecc94b6e0b -
Trigger Event:
push
-
Statement type: