Skip to main content

Loom

CI PyPI Python Licence

Loom is built and maintained by one person; if it saves your team time, consider sponsoring its development.

Loom is an authoring-time CLI for dbt projects: declare each repeated transformation once, and Loom generates the plain dbt models, tests and properties for it, proves they still match what you declared, and lets you search the whole project before you build something that already exists.

The 30-second example

One small declaration is the whole idea. A pattern is a named, reusable shape; an instance is one concrete model declared against it. This declaration:

pattern: current_hub
instances:
  - name: hub_customer
    params:
      description: Stable customer identity table.
      input_models: [stg_customers]
      business_key: customer_id

generates this model:

-- Generated by Loom
-- Generator: hub
-- Hash function: sha256
{{ config(materialized='table') }}
SELECT DISTINCT
    LOWER(sha256(UPPER(TRIM(CAST(customer_id AS VARCHAR))))) AS customer_hk,
    UPPER(TRIM(CAST(customer_id AS VARCHAR))) AS customer_id
FROM {{ ref('stg_customers') }}
WHERE customer_id IS NOT NULL

That is the loop in miniature: the shape is declared once, the model is generated as plain dbt with no Loom macros to call at build time, and the same declaration is what validation, search and coverage read. The first project walks the loop end to end.

Why Loom?

Most dbt projects repeat the same modelling shapes until consistency becomes the problem: copies drift, generators demand a permanent place in your build, and nobody can say with confidence what already exists. Loom's answer, in one line per problem:

Problem Loom's answer
The same shapes copied model after model, drifting apart Declare the shape once as a pattern plus one small instance per model; regenerate all of them from the declaration, and let generate --check prove none of them drifted
Generators that become permanent runtime dependencies Loom runs at authoring time only; the generated SQL/YAML is plain dbt with no Loom macros, so dbt runs your project without Loom installed
Tooling that covers one paradigm and bends the rest to fit Generators for Data Vault, Kimball, medallion, 3NF and more under one contract, mixed freely in one project, never converted into each other
Migrations that demand everything convert at once Convert first, then register the rest: drafts where a generator fits (an equivalence proof gates any swap), authoring drafts with one named hole for layered models that fit none, and registration last, because registering a model withdraws it from the drafting pool
"Does this already exist?" answered by grepping and guessing One inventory of every model in the project, generated and hand-written, searchable in the terminal before you build
Model knowledge scattered across YAML, docs and heads One declaration in your git is the authored source; properties, validation, search and coverage all derive from it

And the guarantee the table rests on: what Loom generates is plain dbt, so you can delete Loom and the project still builds. Adoption is reversible by construction, not by promise.

What Loom is

Loom is three small ideas and four verbs. A generator is the machinery that knows how to build one kind of model (a hub, a staging model, a fact). A pattern is a named, reusable configuration of a generator: the shape your project repeats. An instance set is a collection of instances - concrete models declared against a pattern, supplying only what differs: names, inputs, keys. Hand-written models are not second-class: a registration describes one in the same terms, and it joins the same inventory. The terminology draws the exact boundary between Loom authoring and dbt resources.

Declare, generate, prove, ask. Declare the project once, as patterns, instances and registrations held in your own git. Generate the models, tests and properties from that declaration. Prove the generated files still match the declaration, and that the inventory covers the project. Ask the whole project questions (what exists, what is keyed on this, what would this change touch) before building anything. Generation is episodic; asking is the daily habit.

What generation adds

A generated model is more than its SELECT statement. The generators also produce, from the same declaration and without being asked, the parts of a well-kept model that projects otherwise remember inconsistently or not at all:

  • Columns: the hash and surrogate keys a paradigm runs on (hub and link hashes, dimension surrogate keys), the __loaded_at load timestamp on generated models, and record-source provenance where the generator carries it.
  • Tests: each generator emits the dbt data tests its own guarantees rest on, default-on and not suppressible: unique and not_null on hub and link hashes, relationships from every link endpoint hash to its hub, not_null on fact dimension keys and measures, the dedup key's unique and not_null on silver, not_null on every gold and mart measure, and their counterparts for the other generators (the full table is in the architecture document). Links additionally carry a guard inside their SQL that fails the build on null, blank or unmatched keys rather than letting a bad join through quietly.
  • Properties and declarations: a properties document per generated model carrying its description and column documentation from the declaration, so the catalogue dbt shows is written from the same source as the SQL; the dbt source declaration for any source declared inline in metadata; and, for satellites, the generated current-state view alongside the history table.

The boundary is deliberate: Loom originates only the tests its generators' guarantees require. There are no inferred tests on staging models or raw sources, and no business-specific tests: those stay yours to declare, and tests you declare on an instance (generic tests, and dbt unit tests via the unit_tests parameter) are carried into the generated documents verbatim.

What Loom isn't

  • Not a dbt replacement, and not in your build. Loom runs at authoring time on your machine; the project it leaves behind is plain dbt that builds without it. There are no Loom macros to install into a dbt project and no runtime component at all.
  • Not an orchestrator or a warehouse tool. It schedules nothing, connects to no warehouse to generate (generation needs no warehouse connection), and never runs your models: dbt and your platform do that.
  • Not an ingestion tool. It moves no data. Sources, seeds and loading are yours; staging binds the sources you declare.
  • Not a general-purpose SQL generator. It generates the shapes its eighteen generators know. Models outside those shapes stay hand-written and are registered, not forced into a fit, and the metadata vocabulary never evaluates arbitrary SQL or Jinja on your behalf.
  • Not a migration machine. Conversion is conservative by design: it drafts what provably fits, leaves a named hole where a person must author, and never swaps a model in without an equivalence proof. It will not restructure your project to make it convertible.
  • Not a data-quality platform. The tests it emits are paradigm guarantees (above). Business rules, anomaly detection and observability remain your tooling's job, alongside the tests you declare yourself.
  • Not a hosted service. It is a local CLI working on files in your git. There is no account, no server and nothing to phone home to.

Status and scope

Loom is Alpha software, and this section is the honest boundary of what that means today, stated here in full rather than by reference. Its interfaces (the CLI, the metadata format, the generated output) may change between releases, and changes are recorded in the changelog. What does not change is the exit guarantee above: generated output is plain dbt, so no Loom change can strand a built project, and if a second metadata format version is ever introduced it ships only with its migration helper in the same release, so declared metadata is never left without a migration path.

Dialect support has exactly two states. Executed and supported: DuckDB. The full lifecycle (generate, seed, build, runtime tests) runs on DuckDB, in CI. Generation only (parse-checked, not executed): PostgreSQL, ClickHouse, Databricks, Snowflake, BigQuery, Redshift, Azure Synapse. Loom generates dialect-flavoured SQL for these dialects and the test suite parse-checks every generator's output under each dialect's grammar; nothing is executed on them. That is a syntax guarantee, not adapter compatibility or a support claim, and no other dialect statement anywhere in this documentation should be read as widening it.

Install

Loom needs Python 3.11 to 3.14 and installs from PyPI:

python -m pip install loom-dbt

Loom does not depend on dbt or on any adapter: install those the way you already do for your project (the examples use dbt-duckdb, the executed dialect). Check the install with loom --version, then take one of the two doors below.

Two doors

There are two ways in, depending on whether the project exists yet. Start from the question you actually have, and take the matching door.

New project: work through the first project, a complete project in fourteen authored files (one source, two staging models, one hub, one hand-written model) whose README walks the loop step by step: what to run, what you should see, and what just happened. When the loop makes sense, the quickstart showcase is the second project: every paradigm composed in one project, with the add-a-model recipe for day-two work. Both examples run locally with no credentials, external data or dbt packages.

Existing project: the adoption playbook converts first and registers the rest, and the order is load-bearing. loom convert drafts a conversion for every model unknown to Loom that fits a generator, and an authoring draft (inputs pre-wired, the transform left as one named hole) for layered models that fit none; nothing is swapped in until an equivalence proof passes. Then loom register inventories the models you are keeping hand-written, making them searchable and coverage-checked, and withdraws them from convert's drafting pool: that withdrawal is why registration comes second, and --include-registered re-opens a registered model when its circumstances change. A project may stay hybrid forever, and many should. The jaffle-shop example shows the whole pathway end to end on dbt's canonical example project: what converted, what was registered instead and why, and a verified remodel of the models that stayed hand-written, for teams weighing that trade.

What you can model

If a shape is in this table, a generator ships for it today. The architecture links are the per-generator contracts and guarantees.

What you need Current capability
Project and rename raw columns Staging, using an authored native dbt source; optional project-authored column macros
Represent identities and relationships Hubs, including multi-source hubs (input_models with per-source provenance, the mapping form of record_source), opt-in persistent retention (incremental append on every dialect; merge on Databricks) and first-insert provenance (record_source, __loaded_at, with semantics per generator in Provenance); links join on hub hashes across two or more endpoints, fail the build on null, blank or unmatched keys, and are runtime-verified non-empty on DuckDB, plus dependent-child keys and a non-historised event-link variant carrying payload
Store descriptive vault history Satellites (standard, non-historised, record-tracking, multi-active and effectivity variants over hub or link parents, with generated current-state views), point-in-time tables, bridge tables and reference data constructs
Track dimension history SCD2 snapshot and view, executed on DuckDB, not detached MERGE
Build lakehouse pipelines Medallion bronze (raw load), silver (deduplicated), gold (aggregates)
Build dimensional models Kimball facts with measures and dimension-key columns, dimensions with surrogate keys
Build normalized tables and views 3NF normalized tables with entity/attribute separation, reporting views over normalized inputs
Keep discovery current Validation, generation, manifest-backed coverage and search, including hand-written models
Review more advanced changes Generated dbt tests, source-structure inspection/baselines and migration planning

The daily workflow

Day to day, Loom is four commands against one declaration. Declare: edit a pattern, instance or registration (small YAML files under metadata/ and local/registrations/), then run loom validate, the gate every other step builds on. Generate: loom generate previews every file it would write, change or leave alone; add --write to apply the preview, and --select <selector> to scope it to a set of models. Prove: loom generate --check fails when any generated file no longer matches the declaration, and loom check runs validation plus manifest-backed coverage in one gate; both belong in CI. Ask: loom search finds what already exists before you build it: with no query it prints a project overview, and a query searches the one inventory (declared metadata and the dbt manifest as a single corpus) by name, key, column, input, generator, paradigm, owner or status, with faceted counts over the result set and a detail view when exactly one object matches. Search and the action commands share one selector grammar, so the string that finds a set in search is the same string you pass to generate or check with --select; search may add clearly labelled close matches by shape overlap (and --similar-to <model> searches by example), while --select on an action command always takes exact matches only.

Authoring with an AI assistant

Loom is designed to be worked by an AI assistant as much as by hand. The authored surface is small, structured YAML that diffs cleanly; every command reports as JSON as well as text; and generation is deterministic, so an assistant can draft and propose while the same gates a human uses (validate, generate --check, the equivalence proofs) arbitrate what lands. To make that practical, six agent skills ship inside the package: task guidance that teaches an assistant Loom's model, its commands and its working rules, version-matched to the CLI by construction.

Skill What it covers
loom-core The shared foundation: the mental model, the responsibility boundary (an assistant drafts and proposes; the practitioner decides), write-safety and the selector grammar
loom-bootstrap Starting a brand-new Loom project from zero
loom-adoption Assessing an existing dbt project: convert what fits, register the rest, prove equivalence before any swap
loom-authoring Extending a project already on Loom: new models, new patterns, tests on existing models
loom-ask Read-only questions over the inventory: what exists, what depends on what, whether something like it already exists
loom-maintenance Keeping an adopted project healthy: drift, coverage findings, diagnosing validate and generate failures

Installing and using them. The skills are ordinary Agent Skills (the open SKILL.md format). They are already on disk wherever Loom is installed, inside the package at loom/.agents/skills/, and are mirrored at skills/ in this repository. Install them from there with your agent harness's skill mechanism (for example npx skills add lincolnp-oss/loom-dbt), or copy the six directories into your project's .agents/skills/ folder; then just ask your assistant for the task ("adopt this project", "add a hub for orders", "what already exists like this") and the matching skill loads its procedure. The working rules the skills enforce are the review-first protocol: the assistant reads the project through Loom's JSON outputs rather than guessing its structure, drafts metadata YAML only and never writes generated files, gates every draft on loom validate, and leaves generate and any overwrite of a practitioner file to you. The full protocol is in the architecture document's Migration and AI-assisted authoring section.

Learn more

Task Start here
Start a new project The first project, the guided on-ramp
Add a model to a Loom project The add-a-model recipe
Look up a metadata format or option The metadata reference: a pattern definition and one composite instance as annotated working examples
Migrate an existing dbt project The adoption playbook
Prove a project still matches its declaration loom generate --check and loom check
Look up a command or the selector grammar The command reference
Work with Loom through an AI assistant The bundled agent skills - loom-core, loom-bootstrap, loom-adoption, loom-authoring, loom-ask and loom-maintenance - shipped inside the package at src/loom/.agents/skills/ and mirrored at skills/ in this repository
Understand the design The architecture document and the decision records
Diagnose a failure Diagnostics and recovery
Report a problem SUPPORT.md

A note on how Loom was built

In the spirit of the section above: Loom itself was developed with substantial AI assistance. Design exploration, implementation, tests and documentation were drafted and iterated with AI agents, under human direction and review, using much the same discipline the bundled skills teach an assistant working in a Loom project: small reviewable changes, explicit decision points, and automated gates that a change must pass before it lands.

The appropriate caveat is that AI involvement is a fact about the process, not a quality claim in either direction. The evidence you can judge is the same evidence any project offers: a test suite of over 1,700 tests runs in CI on Linux and Windows across Python 3.11 to 3.14, together with DuckDB runtime integration tests, an installed-consumer test and an end-to-end quickstart run on every change; the release tree was additionally put through an independent readiness assessment and exercised end to end by a separate local AI agent before publication. And the structural protection does not depend on trusting any of that: what Loom generates is plain dbt, reviewable like any other code, and your project keeps building if you delete Loom entirely. If you find a defect, an issue with a reproduction is the most useful contribution there is.

Contributing and licence

Report problems via GitHub Issues; SUPPORT.md has what to check first and what a useful report contains, and no support response is promised. To contribute, see CONTRIBUTING.md for setup, checks and change expectations. Loom is released under the MIT License; files Loom generates into your project are yours under your own terms, and Loom claims no additional rights in them.

Metadata

Release files for loom-dbt 0.1.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 loom-dbt 0.1.0
File Size Uploaded Provenance Actions
loom_dbt-0.1.0.tar.gz 392.0 kB With provenance Details for loom_dbt-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for loom-dbt 0.1.0
File Interpreter ABI Platform Actions
loom_dbt-0.1.0-py3-none-any.whl Python 3 none any Details for loom_dbt-0.1.0-py3-none-any.whl

Total release size: 821.3 kB

Release files / loom_dbt-0.1.0.tar.gz

Download URL loom_dbt-0.1.0.tar.gz
Size 392.0 kB
Tags Source
SHA-256 checksum
How to use checksums
e556cb120fb1fff8d7b21f7d4c6d1151382a9aedd71d4cdc963edfcbf5b31f6e
BLAKE2b-256 checksum
How to use checksums
b0549a9421aae6ffff00da97800fd64f535a6a4a4c90d99525e872cb87080492
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Nov 6, 2026.

Transparency log

Release files / loom_dbt-0.1.0-py3-none-any.whl

Download URL loom_dbt-0.1.0-py3-none-any.whl
Size 429.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5a0d8f6fcc93c30ed3a573b27a4ef67cf5277fcbb05c7e483153a90128d5ac81
BLAKE2b-256 checksum
How to use checksums
e8048cf9749df009021c0f425546d0efd5bf57e1be5f0c066446a9041eb31ea4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Nov 6, 2026.

Transparency log

Security for loom-dbt 0.1.0

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.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for loom_dbt-0.1.0.tar.gz

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 Nov 6, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

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