Skip to content

Quick Start

Scaffold the project with marivo init (see Installation), then author your declarations. A minimal Marivo project has a manifest, datasource declarations, and semantic declarations:

your-project/
marivo.toml
models/
datasources/
warehouse.py
semantic/
sales/
_domain.py

Declare a datasource:

import marivo.datasource as md
import marivo.semantic as ms
md.duckdb(
name="warehouse",
path="warehouse.duckdb",
ai_context=ms.ai_context(
business_definition="Local DuckDB warehouse for sales analysis.",
guardrails=["Use only for development or approved local analysis."],
),
)

Declare semantic objects:

import marivo.datasource as md
import marivo.semantic as ms
ms.domain(
name="sales",
ai_context=ms.ai_context(
business_definition="Sales order analysis domain.",
guardrails=["Revenue metrics should only use completed orders."],
),
)
orders = ms.entity(
name="orders",
datasource=md.ref("datasource.warehouse"),
source=ms.table("orders"),
primary_key=["order_id"],
ai_context=ms.ai_context(
business_definition="One row per sales order.",
guardrails=["Exclude cancelled test data in metric definitions."],
),
)
region = ms.dimension_column(
name="region",
entity=orders,
column="region",
ai_context=ms.ai_context(
business_definition="Sales region assigned to the order.",
guardrails=["Do not treat missing region as a real region."],
),
)
order_date = ms.time_dimension_column(
name="order_date",
entity=orders,
column="order_date",
granularity="day",
is_default=True,
ai_context=ms.ai_context(
business_definition="Calendar date when the order was placed.",
guardrails=["Use this as the default time axis for order metrics."],
),
)
amount = ms.measure_column(
name="amount",
entity=orders,
column="amount",
additivity="additive",
unit="CNY",
ai_context=ms.ai_context(
business_definition="Completed order amount at row level.",
guardrails=["Use only where order amount is already net of cancellations."],
),
)
revenue = ms.aggregate(
name="revenue",
measure=amount,
agg="sum",
ai_context=ms.ai_context(
business_definition="Total completed order amount.",
guardrails=["Use only where order amount is already net of cancellations."],
),
)

You don’t have to write these by hand. marivo init installed the marivo-semantic skill into .agents/skills/, .claude/skills/, and .codex/skills/, so a coding agent running in the project can build the semantic layer with you. Point it at your tables and ask:

Use the marivo-semantic skill to model the orders table in the sales domain.

The skill walks the agent through a disciplined authoring loop, one object at a time:

  • Follow the ladder — domain → entity → dimension → time dimension → measure → metric → relationship — so each object’s dependencies exist before it.
  • Plan, then write. For each object the agent calls the matching ms.prepare_* API and branches on the returned brief before writing the declaration into models/semantic/<domain>/_domain.py. The default metric path is ms.prepare_measure(...), write and verify the measure, then declare ms.aggregate(...); ms.prepare_metric(...) is for metric-level context when a measure-backed workflow needs it.
  • Verify before advancing. After each object it runs ms.verify_object(ref) and does not move on while that fails.
  • Gate at closeout. It finishes with ms.readiness() and resolves blockers before handing the catalog to analysis.

The agent only asks you the decisions it cannot infer from the data or project docs (for example, whether an amount is already net of refunds). Everything it produces is the same Python declarations shown above — reviewable in git like any other code.

Discover the catalog before analysis:

import marivo.semantic as ms
catalog = ms.load()
catalog.list().show()
report = catalog.readiness()
if report.status == "blocked":
report.show()

Run an analysis session after the catalog is ready:

import marivo.analysis as mv
session = mv.session.get_or_create(name="revenue-check", question="Why did Q4 drop?")
catalog = session.catalog
revenue = catalog.get("sales.revenue")
region = catalog.get("sales.orders.region")
current = session.observe(
revenue,
timescope={"start": "2026-10-01", "end": "2027-01-01"},
grain="month",
dimensions=[region],
)
baseline = session.observe(
revenue,
timescope={"start": "2025-10-01", "end": "2026-01-01"},
grain="month",
dimensions=[region],
)
delta = session.compare(current, baseline)
attribution = session.attribute(delta, axes=[region])
attribution.show()
delta.contract().affordances # mechanical compatibility, not recommendations
delta.quality_summary # cheap metadata projection
quality = session.assess_quality(delta)
quality.show()

For custom Ibis work that must re-enter Marivo’s typed metric flow, use session.derive_metric_frame(...). Semantic refs identify metric and axis bindings, while query output columns are plain strings. Across follow-up scripts, recover previous artifacts with session.frame_summaries() and session.get_frame(ref) instead of re-running upstream queries. Use artifact.contract().affordances for mechanical compatibility facts.

The same project structure scales from a one-off script to a reviewed, shared analysis project. Three habits make the difference.

Build the semantic layer as a shared knowledge base

Section titled “Build the semantic layer as a shared knowledge base”

The semantic layer is not just plumbing to reach tables — it is the knowledge base an agent reads before it analyzes. Invest in ms.ai_context(...) on every object:

  • business_definition — what the metric or dimension means, in business terms.
  • guardrails — rules an agent must respect: required filters, exclusions, scope limits.
  • synonyms and examples — so an agent resolves a natural-language question to the right object instead of guessing.

A well-enriched object answers an agent’s “can I use this, and how?” without a human in the loop. Readiness enforces the floor: a missing business_definition blocks analysis, and missing guardrails raises a warning. See the Semantic Layer for the full ms.ai_context(...) contract.

A Marivo project is plain text: marivo.toml plus the Python files under models/. That makes the semantic layer a reviewable, shareable artifact — treat it like application code.

  • Version the contract. Commit marivo.toml and models/. Every change to a metric definition or guardrail shows up as a diff.
  • Review semantic changes like code. Land definition changes through pull requests so a domain owner approves what a metric means before agents use it.
  • Share through the repo. Anyone who clones the project — and any agent that runs in it — gets the same trusted catalog.
  • Keep state and secrets out of git. Add .marivo/ to .gitignore: it holds project-local session and evidence state, not the contract. Credentials are authored as *_env references and resolved from the environment (or cached in user-global ~/.marivo/secrets.toml) — they are never written into the project.

A typical .gitignore:

.marivo/

Run ms.readiness() after loading and resolve blockers before any analysis session. A project can load while readiness is still blocked, so never pass a blocked catalog to an agent. See Readiness.