Skip to main content
Your Power BI semantic model holds the definitions your business runs on — tables, relationships, and DAX measures. Credible reads the model definition, re-expresses its DAX logic as Malloy, and — where you connect it — validates each measure against Power BI’s own query engine before you cut over.

What Credible Reads

Your tabular model — tables, columns, relationships, and DAX measures — from its text definition in TMDL (Tabular Model Definition Language, the folder-based format inside a PBIP project) or the older TMSL. Hand the agent those files and it can translate. Connecting the official Power BI Modeling MCP server (which loads TMDL) or the XMLA endpoint is optional: it lets the agent load the model live and run DAX for validation.

What Comes Across

The everyday modeling carries over. Here’s how the bigger pieces land — and where they get better:

The Migration Flow

Credible reads the TMDL, translates tables and relationships to Malloy sources and joins and DAX measures to Malloy measures, enriches with #(doc)/#(index) tags, and — where you connect it — validates row-by-row using the ExecuteQueries API to run the original DAX and diff it against the migrated result.

What Credible Handles

  • Filter context and CALCULATE — a simple CALCULATE([Total], Status="shipped") is an equivalent filtered aggregate in Malloy. DAX that removes or overrides context (ALL, REMOVEFILTERS) has no ambient equivalent and is re-modeled as an all()/level-of-detail aggregate or a separate query — explicitly, never guessed.
  • Time-intelligence (TOTALYTD, SAMEPERIODLASTYEAR) depends on a marked date table and the visual’s current filter. Malloy has no ambient filter context, so year-to-date is not a measure — it becomes an explicit cumulative view driven by the query’s date grain.
  • Implicit measures — auto-aggregations that were never stored as named measures are materialized explicitly, so parity checks don’t miss them.

Before & After

A TMDL table (relationships live in a separate relationships.tmdl):
CALCULATE with a simple predicate becomes a { where: … } filtered measure; TOTALYTD — which relied on ambient filter context — becomes an explicit cumulative view. The formatString masks map to # currency/# percent. More than a reformat. The migration leaves DAX filter-context complexity behind for aggregates that are correct by construction, and your logic becomes portable, AI-discoverable code instead of a Power BI artifact. See what you gain →

Next Steps

Migration Overview

The method behind every migration

Access Control

Re-establish RLS as governed access rules