Skip to main content

preflight

Stop your AI analyst from silently answering the wrong question.

Static, cross-layer ambiguity detection for governed analytics. preflight reads your dbt, Cube, or MetricFlow definitions and flags the pairs that read alike but resolve to different numbers, before an agent (or a person) grounds a question on the wrong one. It runs on definitions alone, before any query. No warehouse, no model.

CI License: MIT python 3.11 | 3.12 | 3.13 Ruff Checked with mypy

Quickstart · Finding catalog · Library · CI / guardrail

A governed model (dbt, Cube, or MetricFlow) feeds preflight scan, which flags a SCOPE_TRAP: food_orders is orders plus a hidden filter, cited to orders.yml:139

Point it at a dbt project and it finds real traps. Here is one in dbt-labs' own Semantic Layer template, found in seconds and cited to the line an analytics engineer edits:

$ preflight scan . --dialect dbt-manifest
14 findings — high 5, medium 0, low 9

HIGH (5)
  models/marts/customer360/orders.yml:139: [SCOPE_TRAP] food_orders[sem]  ~  large_order[sem]  ~  orders[sem]  ~  ...
      same measure; 'large_order' is 'orders' plus a filter — bare question silently scoped, swap invisible
  models/marts/customer360/order_items.yml:39: [CONCEPT_FORK] food_revenue[sem]  ~  drink_revenue[sem]  ~  revenue[sem]
      same entity/table aggregated the same way over different columns — a bare concept resolves to different numbers
  ...

food_orders is orders with a hidden filter, so a bare "how many orders?" silently under-counts, and the number looks completely plausible. That is the failure preflight exists to catch.

Why it helps. When two plausible metrics exist for one question, an AI agent (or a hurried human) can silently pick the wrong one, and the number looks fine. preflight finds those forks in CI, each cited to the source .yml/.sql line, so you fix them before they ship. It covers the selection half of grounding safety: two valid definitions exist and the wrong one gets picked. (Whether a single definition is internally correct is a separate job.)

Install

uv tool install preflight-analytics                 # or: pip install preflight-analytics
uv tool install "preflight-analytics[embeddings]"   # + the sharper, validated gate (docs/gate.md)

The distribution is preflight-analytics (the bare preflight name was taken on PyPI); the import package and the CLI command are both preflight.

Use

preflight scan . --dialect dbt-manifest            # scan a dbt project
preflight scan . --dialect dbt-manifest --detail   # + the offending source line

Every finding is cited to path:line. New here? docs/dbt-quickstart.md walks a real dbt project (jaffle shop) end to end in a few commands.

What it finds

type one line
SCOPE_TRAP a metric is another metric plus a hidden filter
CONCEPT_FORK one concept, several metrics over different columns
GRAIN_MISMATCH a number that should not be added up over time, offered per day
DEFINITION_DIVERGENCE one term defined two ways, or a metric its docs never describe
NAME_COLLISION two names read alike, or one column name reused across tables
DUPLICATE the same thing under two names
VERSIONED_TWIN users beside users_v2 or a dated backup: a version or leftover, and nothing marks which is current
SIBLING the same measure under two incomparable scopes

Each is a real confusion that returns a wrong number. A worked example and the recommended fix for every one: docs/catalog.md.

How it decides

preflight reads what each metric or column actually means (which rows it counts, which column it adds up, and how), not just its name. Then it looks for two definitions someone (or an AI agent) could reasonably mix up and asks one question: would they return different numbers? If yes, it flags the pair and points at the trap.

The idea in one line: it judges by meaning, not spelling. Two metrics with nearly the same name can be perfectly fine, and two with different names can quietly disagree. It's the second case that burns you.

  1. Read each definition into a plain shape: what it measures, how it's aggregated, from which table, over which rows.
  2. Pair up the ones worth comparing: names a reader could confuse.
  3. Judge from the shapes, not the names: same measure but one is a filtered slice of the other → a scope trap; same table and math over a different column → a forked concept; one term defined two ways → a divergent definition. One rule per kind of confusion.
  4. Report: grouped, worst first, each cited to the exact file and line.

What that looks like on real definitions:

SCOPE_TRAP: a metric that is secretly a filtered slice of another
    users         =  count of all users
    active_users  =  count of users active in the last 30 days
    → active_users is "users" plus a hidden filter, and smaller. Ask "how
      many users?" and you can silently get the active count instead.

CONCEPT_FORK: one name, but computed from different columns
    revenue        =  SUM(amount)
    net_revenue    =  SUM(net_amount)
    gross_revenue  =  SUM(gross_amount)
    → same table, same SUM, three different columns. "Revenue" is not one
      number. Which column did you mean?

It runs no model and no queries, so the same definitions always produce the same findings. (An optional smarter matcher also catches synonyms like revenuesales, but it only widens what gets compared, never the final call.)

dbt

dbt parse                                # your dbt, your profile -> target/manifest.json
preflight scan . --dialect dbt-manifest

Generating the manifest is your dbt project's job; preflight only reads it (if you already use dbt, CI has produced it). Full walkthrough, both jaffle projects, and the manifest details: docs/dbt-quickstart.md.

Not on dbt? preflight reads Cube models directly (no build step) with --dialect cube (walkthrough: docs/cube-quickstart.md), plus raw dbt model SQL (--dialect dbt), MetricFlow YAML (--dialect metricflow), and a native semantic/warehouse/docs layout (--dialect env).

Library

from preflight import scan
for f in scan("path/to/environment"):
    print(f.danger, f.type, f.note)

Adapters, detect_collisions, DetectConfig, and JSON output: docs/library.md.

Guardrail (CI / pre-commit)

preflight scan . --dialect dbt-manifest --fail-on high   # non-zero exit on a HIGH finding

Drops into GitHub Actions or a pre-commit hook. Setup in docs/ci.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

preflight_analytics-0.3.0.tar.gz (44.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

preflight_analytics-0.3.0-py3-none-any.whl (40.2 kB view details)

Uploaded Python 3

File details

Details for the file preflight_analytics-0.3.0.tar.gz.

File metadata

  • Download URL: preflight_analytics-0.3.0.tar.gz
  • Upload date:
  • Size: 44.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for preflight_analytics-0.3.0.tar.gz
Algorithm Hash digest
SHA256 b739e40c7b8aa51155567a085a0aa45dedd91d640646a632cf7460b89f6bfc17
MD5 a4587efa7190aebe7c498fbddc1cdadf
BLAKE2b-256 70875013a30e2cda6d3091d029a9eda7e5ff59194ba9590070e7e3d44c081ab9

See more details on using hashes here.

Provenance

The following attestation bundles were made for preflight_analytics-0.3.0.tar.gz:

Publisher: publish.yml on d-n-ust/preflight-analytics

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file preflight_analytics-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for preflight_analytics-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8856ea31f67d11bb09bba17d0caa5ea2e274a6efdc7cb280f1a6a6cf35ef1ac6
MD5 32b242b3ade693b01a002afdedf68a0b
BLAKE2b-256 6f9fb66d133511db430bbedc2adf763fa7521325d910cadb6a38ce7bc81cd1df

See more details on using hashes here.

Provenance

The following attestation bundles were made for preflight_analytics-0.3.0-py3-none-any.whl:

Publisher: publish.yml on d-n-ust/preflight-analytics

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.4.0

2 files

This release

0.3.0 This release

2 files

0.2.0

2 files

0.1.0

2 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