One metric, end to end
Follow Acme's net revenue from a BigQuery table to an answer and back, through every screen a data analyst touches.
This page follows one metric, Acme's net revenue, through everything Tattva does with it, from the table in BigQuery to a business user's answer, and on to changing and maintaining it. Net revenue is a good example because it uses almost every feature: it settles after 21 days, it refers to another metric (rto_rate), it has a warehouse input that a value grouping widens, and it belongs to the Revenue family. Each step shows the screen, says what to look for, and links to the page that covers it in depth.
Terms used here are defined in Metric rules: the concepts.
1. Connect the table and describe its columns
Acme's order items live in acme-warehouse.sales.fact_order_item, one row per order item. Stored definitions run on BigQuery, so connect it from there.

Open Sources › BigQuery, pick the sales dataset, click fact_order_item, and click Connect as source.
Open the source's Schema tab. Check the columns net revenue uses. Click ✎ to correct any.
On Overview, set Granularity to event and Key columns to the item's key.

| Column | Role | Aggregation hint | Why |
|---|---|---|---|
item_revenue_inc_tax | measure, unit currency | sum_safe | The money being summed |
order_date | timestamp, temporal role event_time | — | Periods are filtered on it |
warehouse_name | dimension | — | The value a question narrows by |
sku | dimension | — | RTO rates are per SKU |
shipment_status, cancellation_reason | dimension | — | Used in conditions, never summed |
Deep dive: Connect and describe data.
2. Write the section
Add net revenue to Acme's metric document, next to the gross revenue and RTO rate sections from the example document. This version narrows by warehouse:
## Net revenue
Net revenue is gross revenue less RTO, for the warehouses someone asks
about. The warehouses change with the question. On settled days we take
out the items that actually went RTO. On the last 21 days, which have
not settled, we take out each SKU's RTO rate (from the RTO rate query
above) as an estimate. SKUs with no settled orders use the overall rate.
Net revenue settles after 21 days, like RTO.
```sql
-- Net revenue, Warehouse 1 and Warehouse 2, last 30 days as of 26 Sep 2026
WITH items AS (
SELECT DATE(order_date) AS day, sku,
item_revenue_inc_tax AS revenue, shipment_status
FROM `acme-warehouse.sales.fact_order_item`
WHERE COALESCE(cancellation_reason, '') <> 'cancelled on panel'
AND warehouse_name IN ('Warehouse 1', 'Warehouse 2')
AND DATE(order_date) >= '2026-08-27'
AND DATE(order_date) < '2026-09-26'
),
settled AS (
SELECT sku, shipment_status
FROM `acme-warehouse.sales.fact_order_item`
WHERE COALESCE(cancellation_reason, '') <> 'cancelled on panel'
AND DATE(order_date) >= '2026-08-06'
AND DATE(order_date) < '2026-09-05'
),
sku_rate AS (
SELECT sku, SAFE_DIVIDE(COUNTIF(shipment_status = 'RTO'), COUNT(*)) AS rate
FROM settled
GROUP BY sku
),
overall AS (
SELECT SAFE_DIVIDE(COUNTIF(shipment_status = 'RTO'), COUNT(*)) AS rate
FROM settled
)
SELECT SUM(
CASE
WHEN i.day < '2026-09-05' THEN IF(i.shipment_status = 'RTO', 0, i.revenue)
ELSE i.revenue * (1 - COALESCE(r.rate, o.rate))
END
) AS net_revenue
FROM items i
LEFT JOIN sku_rate r USING (sku)
CROSS JOIN overall o
```What each part of the section becomes:
| In the section | Becomes |
|---|---|
"Net revenue", AS net_revenue | The metric net_revenue, value column net_revenue |
'2026-08-27', '2026-09-26' | Period inputs such as @start and @end |
"the warehouses change with the question", IN ('Warehouse 1', 'Warehouse 2') | A list input such as @warehouses on warehouse_name, compared with IN UNNEST(@warehouses) |
| "settles after 21 days" | Settlement: 21 days |
'2026-09-05' (26 Sep − 21) and '2026-08-06' (30 days before it) | Two dates filled in by code from the first unsettled day: tail start, and tail start − 30 days |
| "each SKU's RTO rate (from the RTO rate query above)" | A reference to rto_rate (Uses:) |
| "gross revenue less RTO" | Built from: gross_revenue, so V8 looks for gross revenue's row filter |
'cancelled on panel', 'RTO' | Stay as written |
The Revenue family section ("There are two revenues … show gross and net revenue") makes net revenue a variant of Revenue.
Deep dive: Write a metric document.
3. Upload the document
Before you upload, check that no generated formula metric is already called net_revenue on Make › Data › Data › Metrics. If one is, delete it first, or V10 will block your card. See Which rule wins.

Save the document as acme-metrics.md. On Sources › Upload, click Upload more and pick it. Wait until it is Ready.
4. Build, and read the Rules Extraction row
Uploading a document raises no banner, so start the build yourself: open Ontology and click Regenerate.

- The panel at the bottom right lists each phase as it starts and finishes.
rules_extractionis near the bottom of the list: scroll the panel to find it. - A clean read is
rules_extractionwith a green tick and no ⚠ lines under it, followed bynet_revenuein Maintain › Review. A line such asacme-metrics.md: net_revenue not proposed — metric definition with no SQLmeans the section produced no card: fix it and upload a new document. - "Ontology rebuilt" appears when the phases finish, but checks after them run for a few more minutes. If the card is not in Review yet, wait and reload.
- The Build Ontology card on Make › Data does not show a build started with Regenerate. If you start the build from the card instead (Build Ontology → or Rebuild), it shows Rules Extraction with its summary, for example "Read 1 document → 4 rule proposals", but no warnings.
Deep dive: Build the ontology.
5. Review the card, check by check
Open Maintain › Review. Find the metric definition card for net_revenue, with the provenance chip rules_doc.

Read it top to bottom:
- Definition SQL: your query with
@start,@end,@warehousesand the two tail dates in place of the literals. Open Document's query and compare line by line. - Parameters:
@warehousesshows typelist, "from the question", "compared with"warehouse_name, and Document hadWarehouse 1, Warehouse 2. The tail dates show "filled in by code: tail start" and "filled in by code: tail start − 30 days". - "Settles after 21 days — a window overlapping the last 21 days is provisional."
- Uses:
rto_rate. Built from:gross_revenue.
Then the checklist. For this section you should expect:
| Check | Expected result for net revenue | If it is different |
|---|---|---|
| V1 One read-only SELECT | ✓ single SELECT | — |
| V2 Parameters declared and used | ✓ lists every @ input | "tail_start needs a settlement period": the 21 days were lost; say it plainly |
| V3 References resolve | ✓ "uses rto_rate" | rto_rate is not in the document or not accepted |
| V4 Compiles against the warehouse | ✓ "reads acme-warehouse.sales.fact_order_item; ~N MB for one day" | Read the evidence |
| V5 Returns a value | ✓ one row, run for one settled day with your warehouses | "no value …" for a day with no orders can be normal |
| V6 Matches the document's query | ✓ your query and the definition return the same value | Any failure: reject |
| V7 Document's SQL fragments present | ✓ "N fragment(s) present". Once V6 passes, a piece the rewrite restructured is not reported | "missing from the definition: …": read the SQL |
| V8 Parent's filters applied | ✓ when gross revenue's 'cancelled on panel' condition was quoted; otherwise "parent has no stated filters to verify" (not run) | Add the parent's exclusion to your query |
| V9 Columns exist and are populated | ✓ "declared columns resolved" | A misspelt or empty column |
| V10 No clash with an existing metric | ✓ "new metric" | "already exists with a different definition — retire or rename it first": a formula metric has the name |
| V11 Mapped columns compared as lists | ✓ "no parameter on a column with value mappings", or, once you have grouped warehouses, "@warehouses bind every mapped spelling" | The query compared warehouse_name with = |
| V12 Derived dates match the document | ✓ "2 derived date(s) reproduce the document's as of 2026-09-26" | "@… gives 2026-09-06, the document used 2026-09-05": check the 21 days and that every query is as of the same day |

Deep dive, with every evidence message: Review what Tattva proposes.
6. Accept, then find it in Make
Accept gross_revenue and rto_rate first, then net_revenue, then the revenue family card (it stays blocked until its variants are accepted).
Open Make › Data, sub-tab Domain.

The Revenue family row comes first, with net revenue nested under it. Net revenue's row reads:

- Variant of Revenue under the name.
- Stored query and ✓ validated with how long ago.
- Inputs: the question's period · tail_start (filled in: …) · rate_start (filled in: …) · warehouses (from the question). The filled-in dates are today's values: today minus 21 days, and 30 days before that.
- "Settles after 21 days — figures for the most recent 21 days are provisional." Retire is refused here, because the definition's tail dates depend on the settlement.
Click Definition →.

The chips read Stored query, value: net_revenue, settles after 21 days. Click Show expanded SQL to see the query with rto_rate expanded. The Today column shows the tail dates as of today. Source quotes your section.
Now check it against a number you trust, with Try it:
Set From 2026-08-01 and To (exclusive) 2026-09-01 for August.
Type Warehouse 1, Warehouse 2 in @warehouses.
Click Run and compare with your own August figure. August has fully settled, so this is the final number.

Deep dive: Metrics in Make.
7. Set the family policy and group the warehouse values
The document set Revenue's policy to show all variants. Confirm it on the family row.

The row reads Said as "revenue", "sales" and When asked for "Revenue": show all variants. To change it, click [change], pick a policy, and click Save (Ontology Admin+). Deep dive: Families.
Acme's first warehouse appears in the data both as Warehouse 1 and under its old code WH1_OLD. Group them so "Warehouse 1" in a question counts both:
Open Ontology › Manage › Knowledge Graph and click the fact_order_item node.
In the drawer, open Schema, find warehouse_name, and click Values.
Pick WH1_OLD, type Warehouse 1 in Group as, and click Group.

From now on, @warehouses = Warehouse 1 runs with Warehouse 1 and WH1_OLD. Deep dive: Inputs and groupings.
8. A business user asks; read the Trail
"What was net revenue for Warehouse 1 last week?" names the variant, so no family step runs. The warehouse is named, so nothing is asked.
Open the answer's Trail, then the Analyze card:
- Parameters for this question:
@warehouses= Warehouse 1 with the sourcenamed in the question; value mapping 'Warehouse 1 → WH1_OLD'. The period inputs come from "the question's period", and the tail dates from "settlement tail start (today − 21 days)". - Compute: the header says stored definition. Last week is inside the last 21 days, so it shows "Provisional: days from … are not settled yet.", and the answer must say so beside the figure. Open SQL to see exactly what ran.

"What was revenue last week?" uses the family word. The Metric family block names revenue, says "showed every variant", and lists both variants after Variants:.

Gross revenue needs nothing more. Net revenue needs warehouses, and the question named none, so the answer stops with Needs clarification: "To compute net_revenue I need … (one or more, separated by commas). Which value?" The common values of warehouse_name are the buttons. The user clicks one or types into Or type your own answer…, and the Trail then shows the source you answered.

Deep dive: How answers work.
9. Change it: replace through a new document
Finance now wants net revenue to leave out orders under 200 in value. You cannot edit a stored definition in place. Write the corrected section in a new document (acme-metrics-v2.md), with the same name net_revenue and the new condition, upload it, and click Regenerate.
The new card starts with "Replaces the accepted definition of net_revenue. Accepting updates it in place — its family membership and edges are kept." Open SQL changed — compare to see Accepted and Proposed side by side. The input list reads "Parameters unchanged." unless you changed an input. In the checklist, V10 reads Replaces the accepted definition with "formula changed".

Accept it. Net revenue stays a variant of Revenue, and its warehouse grouping still applies. Deep dive: Replace a definition.
10. Maintain it
A build never changes a stored definition, but it checks it. Every build dry-runs net revenue against BigQuery. If someone renames shipment_status upstream, the next build raises a HIGH finding in Maintain › Conflicts, under Data Quality: "Stored definition of net_revenue no longer runs". Until you fix it, answers cannot compute net revenue from its definition. Correct the document, upload it, and accept the replacement. The finding clears at the next build where the definition compiles.

Watch the other findings about this metric too: Data Quality conflicts for warehouse values that match no row, and Definitions conflicts when the Revenue family loses a variant.
Before large changes, click Publish snapshot on Maintain › Versions. Restore to vN rolls the whole ontology, metrics included, back to a snapshot. To undo one definition, upload a corrected document instead.

Deep dive: Maintain the ontology.
Next
Metric rules: the concepts
Every kind of metric logic in Tattva in one place, with where it comes from, where you see it, who changes it, how it is checked, how answers use it and what a rebuild does to it.
Connect and describe data
Add BigQuery tables and uploaded files, then describe their columns so Tattva reads them the way you do.