Skip to content
Go back

What's Still Missing From Apache Ossie, and Where This Goes From Here

Updated:

What’s Still Missing From Apache Ossie, and Where This Goes From Here

Part one covered the concept. Part two deployed one model for real against Databricks, Snowflake, and dbt, and found that the numbers agree everywhere, the metadata doesn’t. This one closes the series: the one problem I don’t think the current spec actually solves, where this project goes from here, and what an Ossie path into Power BI would need to look like.

Definitions versus evaluation

Ossie standardizes the schema: a name, a description, an expression per SQL dialect. Evaluation, how that expression actually gets computed once real joins are involved, is deliberately left to whoever’s on the other end of the conversion. That’s not a gap, it’s the same division of labor covered in part one: Ossie is the interchange format, not the runtime, closer to a .proto file than a query engine. What that division actually implies is worth spelling out, though, because it’s easy to read “write once, query anywhere” as a stronger guarantee than the spec is actually making.

Where this project goes next

The Power BI gap, now closed

TMDL

This section originally described a gap and two open pull requests. Both merged, and on 30 September 2026 Microsoft made it official: Christian Wade’s commitment to Apache Ossie on the Power BI blog, timed with FabCon Europe, alongside Snowflake’s engineering post from the other side. Rewritten below around what shipped.

DAX is in the core spec’s dialect enum. There is a converters/microsoft package doing bidirectional Power BI conversion, and the vendor token registered for it is MICROSOFT, not POWER_BI, because Power BI, Fabric, Azure Analysis Services and SQL Server Analysis Services share one object model and a model file cannot tell you which of the four it came from.

Worth keeping the point this section always made about DAX, because shipping does not make it less true. ANSI_SQL, SNOWFLAKE, DATABRICKS and BIGQUERY are all SQL, close enough in shape that this series watched SUM(order_amount) survive from one dialect tag to the next with barely a syntax change. DAX is not a SQL dialect running on a different engine, it is a different kind of language: no SELECT, no FROM, no GROUP BY, calculations defined through row and filter context instead. A converter cannot lean on syntactic similarity the way it can between SQL dialects.

Its answer to that is a deliberately narrow translator: a closed set of shapes with unambiguous DAX equivalents, and a refusal for everything else. Which needs a correction to what I wrote here before. I called the design “refusal-first,” and described it as unsupported constructs being reported and skipped rather than silently machine-translated. That is half right. Refusal is real and loud, but translation inside the supported set happens with no warning at all, and the condition attached to it, that a column must resolve to exactly one dataset, means COUNT(*) already fails in an ordinary two-table model. Assume it covers single-table metrics and write the DAX yourself for the rest.

I have written up what the converter does in detail in a separate post, including the handful of things that will stop a model loading in Fabric. One finding belongs here, though, because it lands directly on the thesis of this series.

A model that passes through Power BI on its way somewhere else arrives without its measures. Convert an Ossie model to Power BI and back, and every metric returns with DAX as its only dialect. The original SQL is not lost, it is preserved as an annotation inside a POWER_BI stash, but no other converter reads another vendor’s stash. Take that document on to Databricks and every metric is dropped for want of a DATABRICKS or ANSI_SQL dialect.

The obvious reading of that is unfair, though. A Power BI model that leaves and comes home is perfect: import then export gives a byte-identical file, and the stash is exactly what makes that work. The same stash is what breaks the onward trip. Fidelity is preserved in a vendor-private channel, which serves a model returning to where it came from and defeats one being handed to anybody else.

The practical rule that falls out: keep your Ossie models as the source of truth and convert outward to each platform. Never feed one platform’s export into another. Which is, conveniently, the same conclusion the rest of this post reaches from a different direction.

The payoff, once this matures, is the one part one argued matters most: grounding an AI agent’s answer. Copilot in Power BI needs the same kind of governed, versioned metric definition Cortex Analyst and Genie already ground their answers in.

How an organization would actually run this

The shape I keep coming back to: one team owns an Ossie repo, git-versioned, PR-reviewed, the way apache/ossie’s own examples are structured. Every other team, every other tool, whether that’s a BI dashboard, a dbt project, or an AI agent answering a business question, consumes from that one source instead of redefining “revenue” locally. Change a metric once, in one reviewed PR, and every downstream converter picks it up. That team can’t be purely technical, either, not if the thing it’s approving is what “revenue” means. Part one’s whole opening was that nobody had written that definition down anywhere shared; owning the repo means someone now has to, and that’s a business call as much as a syntax one.

Apache Ossie logo

That’s not a new idea on its own, a mature dbt-metrics or LookML shop already runs something close to it. What Ossie actually changes is that the PR isn’t locked to one vendor’s format. The same reviewed change ships into Databricks, Snowflake, and dbt at once, instead of getting rebuilt by hand in each. That cuts both directions: migrating off a platform stops being a rebuild-everything-by-hand project, and staying put stops being the path of least resistance just because leaving would mean redoing every metric from scratch. Less vendor lock-in isn’t a side effect here, it’s close to the whole point.

It’s also, unintentionally, an argument for centralizing that this whole series makes without meaning to. Every real bug found in part two, and the two dbt bugs found independently in two separate codebases, traced to exactly one place: one converter, one specific field, one specific model. None of them were diffuse the way the original “three definitions of revenue” problem from part one was, where nobody could say which of three people was wrong, only that they disagreed. That kind of disagreement is exactly why some companies end up running a standing weekly meeting, across reporting teams and platforms, just to reconcile why the same KPI came out different in two dashboards this week. Centralizing on Ossie doesn’t make the converters less buggy. It makes it obvious, immediately, which converter got it wrong, instead of the standing meeting having to find out.

The honest tradeoff: that central team now needs to actually know each target’s specific failures, not just write portable YAML and assume it works. Somebody on that team needs to know that Databricks’ Metric Views won’t auto-qualify a joined column inside a function call, that this converter of dbt’s needs a primary_key column to also be a declared field or it drops the entity silently, that Snowflake wants metrics nested per table with no warning if you get it wrong. That’s real depth per platform, not the “define once, forget about the rest” pitch this series opened with, and it’s exactly the kind of central team that becomes a bottleneck if it’s understaffed relative to how many metrics are actually changing.

Worth naming as a “not yet, but”: this whole series has effectively been running that team’s job by hand, for one tiny model, converter by converter, checking warnings, running real deploys, comparing real numbers. Nothing about that is specific to a demo. Once the spec and its converters are mature enough to trust running unattended, that work belongs in a CI pipeline, not with a person doing it by hand: every PR against the Ossie repo triggers every target converter, fails loudly on new warnings, and, further out, runs a live query against a real (probably scratch) warehouse to confirm the number didn’t move. Not there yet, given how much of this series was three converters each failing in their own way, but it’s a straight line from “one person doing this manually” to “a semantic-model pipeline with its own test suite,” not a reinvention.


Share this post on:

Previous Post
The Apache Ossie Power BI Converter, in Detail
Next Post
What Actually Happened When I Deployed Apache Ossie to Databricks, Snowflake, and dbt