What a Number Means
Compare a measure with an earlier period or a target, say which way is good, choose how the change shows, and run a table calculation such as a running total or a share of the total, through Semaphor MCP.
An agent can set what each number on a card means, in the same words the editor uses: what a measure is compared with, which direction is good, how the change shows, and a table calculation such as a running total or a share of the total. It reads each one in the outline and changes it through semaphor_propose_change and semaphor_apply_change like every other change (see Making Changes).
{
"dashboardId": "d_8f2k",
"changes": [
{
"op": "card.update",
"cardId": "c_purchases_trend",
"query": { "measures": [{ "name": "net_purchase_value", "comparison": { "kind": "previous_year" } }] },
"comparisonDisplay": { "mode": "percent_change" }
}
]
}The proposal reads it back in plain words:
Compares Net Purchase Value in "Net purchase value by month" with the same period last year, and shows the comparison as a percent change.
Read What a Card Computes
semaphor_get_dashboard_details with fields: ["outline"] shows each measure's comparison and table calculation inside the card's query, and the card's comparison settings beside its columns:
{
"cardId": "c_purchases_trend",
"title": "Net purchase value by month",
"type": "line",
"query": {
"measures": [
{ "name": "net_purchase_value", "comparison": { "kind": "previous_year" } },
{ "name": "net_tons", "transform": { "kind": "moving_average", "orderBy": { "field": "purchase_date", "direction": "asc" }, "resetBy": [], "window": 3, "minPeriods": 1 } }
],
"dateField": "purchase_date",
"timeGrain": "month"
},
"comparisonDisplay": { "mode": "percent_change", "precision": 1 },
"columns": {
"Net Purchase Value": { "field": "net_purchase_value", "role": "measure", "polarity": "lower_is_better", "inherits": ["label", "format"] }
}
}A measure with no comparison follows the dashboard's date comparison. A table calculation shows every field it runs with, defaults included. Send what you read back and nothing changes, so an agent can copy a card's query, change one measure, and send it back.
Comparisons and Targets
A measure in query.measures takes a comparison:
kind | What the measure is compared with |
|---|---|
inherit | The dashboard's date comparison, when a viewer picks one. This is the default. |
off | Nothing, even when the dashboard compares |
previous_period | The period before. offsetPeriods goes further back; alignment: "weekday" matches weekdays; toDate: true compares the elapsed part of an incomplete period |
previous_year | The same period last year, with offsetYears, alignment and toDate |
target | A fixed number, targetValue |
{ "op": "card.update", "cardId": "c_total", "query": { "measures": [{ "name": "net_purchase_value", "comparison": { "kind": "target", "targetValue": 1000000 } }] } }- Comparisons show on KPIs, tables and bar, line and area charts; targets on KPIs and tables.
- A period or year comparison needs a date to compare over: the card's date grouping (on a KPI, its Trend By:
dateFieldwithtimeGrain), or the dataset's primary date. - It shows while a date range applies to that date: the card's own
timeWindow, or a dashboard date filter that reaches the card. A proposal says so in its warnings when none does yet. - A measure keeps its comparison when the change doesn't state one, even when the rest of the query changes.
Which Way Is Good
A measure's polarity in columns says whether a rise is good (higher_is_better, the default), bad (lower_is_better) or neither (neutral). It colors the change on that card.
{ "op": "card.update", "cardId": "c_returns", "columns": { "Returns": { "polarity": "lower_is_better" } } }When the semantic model sets a measure's direction, every card follows it: the outline lists polarity in the column's inherits, and a card built through the MCP takes it too.
How the Change Shows
| Setting | Values |
|---|---|
comparisonDisplay | { "show": false } hides the comparison; mode is difference, percent_change, both or ratio; precision sets the decimals of a percent or ratio, 0 to 6, and null returns it to the default |
inheritComparison | false: the card's measures without a comparison of their own ignore the dashboard's date comparison. true follows it again. |
A KPI, its tab header and its tile in a document show the change the same way.
Table Calculations
A measure in query.measures takes a transform, calculated over the rows the card returns:
kind | What the measure shows | Its own fields |
|---|---|---|
running_total | The total so far | |
moving_average | The average of the last window points | window (7), minPeriods (1) |
difference_from_previous | The change from the previous row | mode: difference or percent_change |
percent_of_total | Each row's share of the total | scope: grand_total or partition_total; output: percent or fraction |
orderBynames the grouping it runs along, with an optionaldirection. Left out, it runs along the card's date grouping; a card without one names the grouping to run along.resetBylists the groupings it starts over for. Left out, it starts over for every other grouping.- A share of the total shows as a percent unless the column has a format of its own.
transform: nullremoves it. A measure's own comparison and a table calculation replace each other, and the sentence says so.
{
"op": "card.update",
"cardId": "c_monthly_by_facility",
"query": {
"measures": [{ "name": "net_purchase_value", "transform": { "kind": "running_total", "orderBy": { "field": "purchase_date" }, "resetBy": ["facility_id"] } }]
}
}Table calculations run on tables and bar, line, area and pie charts.
In semaphor_analyze
semaphor_analyze takes the same comparison and transform on its measures. A comparison applies to the whole analysis, so state it once (top level or on the measures), with kind and targetValue. A table calculation runs on the result rows:
{
"domainId": "dom_ops",
"datasetName": "purchase_line",
"measures": [{ "name": "net_tons", "transform": { "kind": "percent_of_total" } }],
"dimensions": ["facility_id"]
}A KPI's Trend By
A KPI's Trend By is its query's dateField and timeGrain. It is the date a comparison runs over, and it sets where a to-date comparison cuts the current period.
{ "op": "card.update", "cardId": "c_total", "query": { "dateField": "purchase_date", "timeGrain": "month" } }What Needs a Yes
A comparison, a target, a table calculation and inheritComparison change what a card computes, so their proposal returns confirm: "needed". A polarity or a comparisonDisplay only changes how a card reads, so on its own it returns confirm: "not_needed".
How Cards Read and Show
Change a card's column headers, number and date formats, hidden columns, widths, total row, data labels, legend, axes, chart variant and highlights, and a dashboard's title, through Semaphor MCP.
Live Changes in Embedded Dashboards
Let your own agent change the dashboard your user is looking at, unsaved, with Undo, using one token for the iframe and the MCP.