Skip to main content

bloomery

Entity-first spec compiler — declarative specs in, SQLMesh, dbt and Cube artifacts out, byte-for-byte the same every time.

PyPI Python CI Coverage OpenSSF Scorecard Docs Licence Typed Ruff

Documentation · Quickstart · Examples · Concepts · Changelog


The number that looks right

In a config-driven data platform the worst failure is not a crash. It is a number that looks right and is wrong.

Shipping is charged once per order. Someone flattens it into a mart at line grain, and SUM(shipping) quietly overstates by the number of lines on each order. The SQL is valid, the join is correct, the tests pass, the dashboard renders — and finance finds it two quarters later. The formula was right, the data was right, the answer was 3× wrong.

That class of bug survives syntax checks, type checks and code review, because nothing in the stack knows that shipping is an order-grain measure. bloomery does, and refuses:

GuardrailError:
  1 error(s):
  - marts: marts.order_items.measures.shipping: measure 'shipping' has grain 'order'
    (one row per order), not the mart's grain 'order_item' (one row per line on an
    order) — measure grain must strictly equal mart grain (RFC 0010 D2). Flattened
    into the mart it is duplicated once per 'order_item' row and any SUM over it
    overstates. Fix: remove it from this mart's measures, or serve it from a mart at
    grain 'order'

Not a warning. A compile error, with the spec path that caused it and the two ways out. The system may fail to answer a question; it may not answer it wrongly without saying so.

What it is

A pure function library. You hand it declarative specs — a catalog, entities, mappings, metrics, marts, and optionally steps — and it returns artifacts:

                            ┌─▶  SQLMesh    models, audits
   catalog.yaml             │
   entity_model.yaml        ├─▶  dbt        models, schema tests
   mapping_*.yaml   ──────▶ ┤
   metrics.yaml             ├─▶  Cube       semantic model
   marts.yaml               │
   steps.yaml               └─▶  MetricFlow semantic manifest

   MetricRequest    ──────▶      SQL over a wide mart + a written explanation

Those specs carry what SQL cannot: grains, units, tax bases, and additivity classes. That is the whole trick — the compiler refuses what a hand-written model would happily run, because it knows what the columns mean, not just what type they are.

Three properties it holds itself to, each enforced by test rather than intent:

Deterministic Same specs in, byte-identical artifacts out — across machines, processes, and PYTHONHASHSEED. No clocks, no randomness, no environment reads, no filesystem.
Fail-closed Grain fan-out, additivity violations, mixed units, contract breaks: compile errors with a source path and a named reason, never a silent wrong number.
Reviewable Emitted artifacts are stable-sorted, pretty-printed text, so a diff of the output is a faithful diff of the semantic change.

Install

uv add bloomery      # or: pip install bloomery

Python 3.12–3.14. No orchestrator, no cloud SDK, no database driver.

The shape of it

Specs go in as strings, artifacts come out as values. Writing them to disk is your loop — the library never touches a filesystem:

from bloomery import Target, compile_project, load_catalog, load_project

catalog = load_catalog(catalog_yaml)
project = load_project({"entity_model.yaml": entities_yaml, "mapping_orders.yaml": mapping_yaml,
                        "metrics.yaml": metrics_yaml, "marts.yaml": marts_yaml})

for artifact in compile_project(project, target=Target.SQLMESH, dialect="duckdb", catalog=catalog):
    print(artifact.path)          # write artifact.content wherever your repo keeps models

The same specs also serve queries. A MetricRequest becomes SQL over a wide, pre-joined mart — planned by an embedded, render-only MetricFlow, and executed by nothing:

plan = planner.plan(project_ir, MetricRequest(metrics=("revenue",), dimensions=("ordered_month",)),
                    dialect="duckdb")
print(plan.sql, plan.explanation.render())

And from a shell — the CLI is a thin argument shell over exactly these functions, and the only part of the package that touches a filesystem:

bloomery compile specs/ --target sqlmesh --dialect duckdb --out out/
bloomery resolve specs/          # what is computable, what is missing, what was refused
bloomery schema  --out schemas/  # JSON Schema per spec kind, for editors and validators

A refusal exits 1 and a bad invocation exits 2, so a pipeline can tell "your spec is wrong" from "your command is wrong". Nothing is executed: bloomery run does not exist.

See it work

Four runnable projects in examples/. None of them asserts anything it does not execute, and the seed data is deliberately not clean — padded emails, five spellings of one segment vocabulary, prices in integer cents, an unparseable timestamp.

Example What it demonstrates Infrastructure
quickstart/ The five core spec kinds; compile, then plan a metric request none
refusals/ Five specs that look right and cannot be right, with the real messages none
targets/ SQLMesh, dbt, Cube and the planner all actually running one container
lakehouse/ Iceberg via Lakekeeper: union merge, quality rules, quarantine, a blocking audit four containers
uv run python examples/quickstart/run.py     # no setup at all
cd examples/targets && just demo             # the interesting one

targets/ is worth the container. It compiles one spec set to SQLMesh and to dbt, builds both, and compares the two marts row for row — because "two frameworks, one spec, one answer" is a claim, and this turns it into a measurement.

What it is not

  • It does not execute SQL. It emits artifacts and plans; SQLMesh, dbt, or Cube run them.
  • It does no orchestration — scheduling, backfills and deployment belong downstream.
  • It does not read your warehouse. Catalogs and profiles are inputs, computed elsewhere.
  • It contains no LLM. Proposals may be drafted with LLM assistance upstream, but they arrive as validated specs, and everything the library emits — including the human-readable query explanations — is generated deterministically.

Status

0.1.0, the first release. Everything the library does ships behind the quality gate: the spec layer over six document kinds, the deterministic IR, transforms and typecheck, resolution, fail-closed guardrails, declarative data quality with quarantine and replay, steps as referenced implementations, wide marts with role-playing dates, the SQLMesh/dbt/Cube emitters over DuckDB/Trino/Postgres, the MetricFlow-backed planner, spec-diff planning, spec assessment, the CLI, and the JSON Schema export. Every test tier runs, including the Docker-backed engine matrix, the target e2e tiers, and the three-way equivalence tier.

From this release the promises in Stability bind: per-kind versioning over spec YAML (fully), SemVer over the Python API (breaking changes are allowed in a minor below 1.0, but never silently), and emitted artifacts explicitly not stable across versions — byte-reproducible for fixed inputs, which is determinism rather than a cross-version promise. Pin the minor if you want the API to hold still.

Designs that have not yet landed live as RFCs in rfcs/; code that contradicts a live RFC is the bug, not the RFC. An RFC is retired once it ships — the code, the tests and the documentation are then the account of what bloomery does.

Documentation

Full documentation: morzecrew.github.io/bloomery

Contributing

Contributions, issues and feature requests are welcome — see CONTRIBUTING.md, including the RFC process for larger changes.

Licence

MIT — see LICENSE.

Metadata

Release files for bloomery 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bloomery 0.2.0
File Size Uploaded
bloomery-0.2.0.tar.gz 1.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for bloomery 0.2.0
File Interpreter ABI Platform
bloomery-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.6 MB

Release files / bloomery-0.2.0.tar.gz

Download URL bloomery-0.2.0.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
d4cbebc6475d05fdb4ded42276705f65c3ad1298cb2f4d6f65395a74aabf2545
BLAKE2b-256 checksum
How to use checksums
ebcaf639bbc4186e27af5d341c7090feb23606056f55b56c9dbead0850996818
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release files / bloomery-0.2.0-py3-none-any.whl

Download URL bloomery-0.2.0-py3-none-any.whl
Size 461.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
74d03ea94ca008cced2c05f973ee61c98072c7a4de30093b22e7c6318ae5156e
BLAKE2b-256 checksum
How to use checksums
c3082b912d329bac68f74d2c6c4396cbb6e237609a88dac94ec7141ea632a30a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.

Transparency log

Release history Release notifications | RSS feed

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page