Skip to main content
A complete, self-contained data-warehouse example: a SaaS subscription business modeled Kimball-style as a Business Vault (dimensions + facts, including a monthly snapshot fact) with a condensed Data Mart layer on top, all in a single bundled DuckDB file. This is the broadest example in the set: it exercises every metric binding kind canonic supports, all four guardrail kinds, finality/restrict-source, query-based assertions, and a global knowledge base, in one project.

Schema

Business Vault (8 dimensions, 10 facts) + 4 data marts:

Setup

Bundled DuckDB file: no server, no credentials.

Quickstart

Unlike the other guides, there’s no canonic ingest --bootstrap step here: every semantic source, metric, and guardrail already ships provenance: human_curated in contracts/ and semantics/, so there is nothing to bootstrap. (Run canonic ingest --dry-run any time to see it reconcile cleanly against the live saas.duckdb schema with zero diffs.)
No LLM is required for any of these commands: this example ships hand-curated semantics and contracts, so canonic never needs to draft anything.

Metric catalogue: all 7 binding kinds

Ratio and weighted-avg components must themselves be single-kind metrics, so a set of small helper metrics (mrr_total, active_accounts, churned_customers, new_customers, won_opportunities, total_opportunities, campaign_spend, discount_value_sum, total_contract_value, nps_net, nps_responses) provide those building blocks. See The semantic compiler for how each kind actually compiles.

Guardrails: all four kinds

  • revenue-excludes-refunds / revenue-excludes-trials: mandatory_filter (error). Injects status != 'refunded' and is_trial = false into every gross_revenue query.
  • board-reporting-final-only: restrict_source (error). In the board_reporting context, confines gross_revenue to the final fct_invoices source, paired with a finality rule (finality-revenue) that declares the final/provisional realizations and coalescing rule.
  • board-reporting-trusted-only: min_trust (error). In the board_reporting context, blocks the query unless the answer’s trust tier meets the declared floor.
  • ending-mrr-requires-month: required_dimension (warn). Requires grouping ending_mrr by snapshot_month. Unlike the guardrails above, warn means an omitted snapshot_month doesn’t block the query, only adds a warnings[] entry.

Assertions

Query-based, with expected values derived from the deterministic seed:
  • gross-revenue-2025-q117814.00 (paid, non-trial, non-refunded invoices, Q1)
  • active-subscribers-2025-0312
canonic assert runs them and reports accuracy (expected: 100%). See canonic assert.

Knowledge

knowledge/global/: bound to semantic entities via sl_refs:
  • mrr-definition (definition, with a live {{ sl:… }} template)
  • semi-additive-mrr-caveat (caveat): never sum MRR across months
  • revenue-excludes-refunds-trials-caveat (caveat)
  • revenue-finality-policy (policy): final vs. provisional revenue
  • ltv-methodology (policy): why customer_ltv is opaque
  • vault-vs-mart (reference): when to use vault facts vs. data marts

Regenerating the warehouse

setup.sql is plain, deterministic DDL + seed data and can also be run directly through any DuckDB client: duckdb saas.duckdb < setup.sql.