This document defines a consistent authoring model for Grafana-like result panels in Altinity SQL Browser.
It is written for two audiences:
- humans editing SQL and
query.specin the workbench; - agents generating or reviewing saved-query Specs.
The central rule is:
SQL owns data. Spec owns presentation.
A result may contain scalar fields or structured ClickHouse named tuples. Named tuples group the multiple runtime values needed to render one visual object. The Spec adds labels, descriptions, units, decimals, colors, thresholds, and other presentation-only behavior.
This guide covers:
- reusable delta semantics;
- KPI;
- stat;
- gauge;
- candlestick;
- confidence interval/band;
- box plot;
- range;
- Gantt;
- histogram;
- heatmap;
- network;
- Sankey;
- health/status.
Some panel types described here are forward-looking design contracts rather than currently implemented registry arms. A saved Spec may preserve unknown future fields, but a build can render only panel types it implements.
A canonical saved query has this application-owned envelope:
{
id,
sql,
specVersion,
spec
}The Spec editor receives only query.spec.
The complete saved-query and Library envelopes are specified separately in the Library JSON Schema guide. Authors generating an importable file should validate the whole document against that contract, not only the inner Spec.
Typical Spec:
{
"name": "Service overview",
"description": "Current production health and traffic.",
"favorite": true,
"view": "panel",
"panel": {
"cfg": {
"type": "kpi"
},
"fieldConfig": {
"columns": {
"request_rate": {
"displayName": "Requests",
"unit": " req/s",
"decimals": 0
}
}
}
},
"dashboard": {
"role": "panel"
}
}The following never belong in the editable Spec:
- query id;
- SQL;
- Spec version;
- export-envelope fields;
- compatibility mirrors;
- transient result data;
- transport format;
- run progress;
- current filter values.
The workbench Spec editor derives its native CodeMirror completion popup from
the canonical query.spec schema.
Typing a property or finite value opens the popup automatically; press
Ctrl-Space to request it explicitly. Arrow keys navigate, Enter or Tab
accepts, Escape closes the popup, and Tab inserts two spaces while the popup is
closed.
Known root/nested keys, panel variants, constants, enums, defaults, examples, and snippets are schema-owned. Result-column names and zero-based indexes appear only at schema-annotated positions and come from the active tab's last successful result. No completion action executes SQL, and the absence of result metadata is not an error. Unknown extension keys remain legal even though the popup does not enumerate them.
This document uses:
- MUST for a required interoperability rule;
- MUST NOT for a prohibited shape;
- SHOULD for the preferred form;
- MAY for an optional extension.
Unknown Spec fields MUST be preserved. Unknown data fields MAY be ignored by a renderer when they are not needed by the implemented panel version.
A top-level result column is either:
- a primitive field, such as a number, string, boolean, date, or timestamp;
- a structured visual object represented by a named ClickHouse tuple;
- an axis, category, label, or key used by one or more visual objects.
The exact role depends on the panel type.
A scalar is appropriate when one value is enough to render the object.
Examples:
SELECT
count() AS active_users,
avg(duration_ms) AS mean_latency_msFor a one-row KPI/stat panel, these produce two independent cards.
A named tuple is appropriate when several runtime values jointly describe one visual object.
Example:
SELECT
(
12.4 AS value,
-1.7 AS delta
) AS cancellation_rate
SETTINGS enable_named_columns_in_function_tuple = 1The top-level column is cancellation_rate. Its runtime object is:
{
"value": 12.4,
"delta": -1.7
}ClickHouse can construct a named tuple from aliased tuple elements when the
query enables named columns for the tuple function:
(expr AS member_name, expr AS another_member) AS result_column
SETTINGS enable_named_columns_in_function_tuple = 1Alternatively, cast a positional tuple to an explicitly named tuple type:
CAST(
(expr, another_expr),
'Tuple(member_name Float64, another_member Float64)'
) AS result_columnAliasing tuple elements without the setting does not establish the result type
contract: ClickHouse reports a positional type such as
Tuple(Float64, Float64).
Only named tuples are used as visual-object contracts. Positional tuples such as (12.4, -1.7) are ambiguous and MUST NOT be interpreted by member position.
There are two main result shapes.
Used by KPI, stat, gauge, and card-style status panels:
one row
├─ object A
├─ object B
└─ object C
Each top-level field becomes one visual object.
Used by candlestick, confidence, box plot, range, Gantt, histogram, heatmap, network, and Sankey:
row 1 → object/event/bucket/edge
row 2 → object/event/bucket/edge
row 3 → object/event/bucket/edge
An axis or category column may accompany one or more named-tuple object columns.
Unless a panel explicitly sorts for rendering, row order is SQL order.
Authors SHOULD use ORDER BY whenever ordering matters.
For one-row multi-object panels, field order is SQL result-column order.
Spec object-key order is not a layout contract.
NULL means unavailable data.
- A missing optional tuple member and a member whose value is NULL are not always equivalent.
- Required members MUST exist in the tuple type.
- Optional runtime values SHOULD use nullable tuple members.
- The Spec may define display text such as
"noValue": "No data".
SQL SHOULD produce values whose numerical meaning is already final.
Examples:
- return
12.4when the card should show12.4%; - return
0.124only when the intended visual formatter explicitly treats it as a ratio; - compute comparison deltas in SQL;
- compute confidence bounds in SQL;
- compute histogram buckets in SQL;
- compute graph edge weights in SQL.
The renderer MUST NOT invent business calculations.
Common field metadata:
{
displayName?: string,
description?: string,
unit?: string,
decimals?: integer,
color?: string,
noValue?: string,
hidden?: boolean,
thresholds?: Threshold[],
links?: Link[]
}fieldConfig.columns is keyed by exact top-level result-column name:
{
"fieldConfig": {
"columns": {
"mean_latency": {
"displayName": "Mean latency",
"unit": " ms",
"decimals": 1
}
}
}
}No generic matcher is required for the initial design. Exact names are easier for humans, agents, validators, and completion engines.
Do not place runtime values in Spec:
{
"fieldConfig": {
"columns": {
"availability": {
"value": 99.95
}
}
}
}A literal value, delta, min, max, open, close, or edge weight in presentation metadata is invalid unless a future panel explicitly defines it as a static visual setting.
Some concepts can be either runtime data or static visual configuration. Gauge range is the main example.
Use one source, not hidden precedence:
- runtime
min/maxtuple members for data-dependent ranges; - static
fieldConfig.columns.<field>.gauge.min/maxfor a fixed display range.
When both are supplied, semantic validation SHOULD report an error rather than silently choosing one.
A query-backed panel owns its result transport format.
The panel implementation MAY choose any ClickHouse-supported output format for which it has a correct parser. The format is not user-editable and is not stored in Spec.
For typed JSON and named tuple objects, the preferred streaming format is:
JSONEachRowWithProgress
with:
output_format_json_named_tuples_as_objects = 1
A user-authored trailing top-level FORMAT clause MUST be rejected before execution when the panel requires an implementation-owned format.
Invalid:
SELECT count() AS requests
FORMAT CSVExpected error:
This panel owns the result format. Remove FORMAT CSV from the SQL.
The SQL text must remain unchanged and no request should be sent.
{
"displayName": "Cancelled flights",
"description": "Share of flights cancelled in the selected year."
}displayName labels the field. description supplies supporting text or a tooltip.
The saved query's top-level spec.name remains the panel/tile title.
{
"unit": "%",
"decimals": 1
}unit is display-only. It does not multiply or divide the value.
decimals controls rendering, not stored precision.
{
"color": "warning"
}A color may be a theme token, implementation-defined palette id, or supported CSS color. Agents SHOULD prefer stable theme tokens when the application defines them.
{
"noValue": "No recent samples"
}Used for NULL or unavailable values.
6.5 Hidden fields
{
"hidden": true
}A hidden eligible field remains in the SQL result and Spec but is not rendered.
{
"thresholds": [
{ "value": 0, "color": "ok", "label": "Healthy" },
{ "value": 200, "color": "warning", "label": "Slow" },
{ "value": 500, "color": "critical", "label": "Critical" }
]
}Thresholds are presentation rules over SQL values. They do not change the values.
Threshold steps SHOULD be ordered by ascending value.
Delta is a reusable member pattern, not necessarily a standalone panel type.
Tuple(
value <numeric>,
delta Nullable(<numeric>)
)
Example:
SELECT
(
current_rate AS value,
current_rate - previous_rate AS delta
) AS request_rate
FROM ...{
"fieldConfig": {
"columns": {
"request_rate": {
"displayName": "Request rate",
"unit": " req/s",
"decimals": 0,
"delta": {
"displayName": "vs previous period",
"unit": " req/s",
"decimals": 0,
"positiveIsGood": true,
"show": true
}
}
}
}
}- positive
deltashows an upward direction; - negative
deltashows a downward direction; - zero is neutral;
- NULL omits the delta indicator;
positiveIsGoodcontrols good/bad coloring, not arrow direction;- the renderer MUST NOT infer the comparison period;
- SQL computes the delta.
Authors must distinguish:
current - previous
from:
(current / previous - 1) * 100
Use units that communicate the calculation:
" pp"for percentage points;"%"for percentage change.
Large, prominent numerical cards for a one-row result.
- exactly one row;
- each eligible top-level numeric scalar or named tuple is one card;
- tuple shape:
Tuple(value numeric, delta Nullable(numeric)?).
SELECT
12.4 AS cancellation_rate,
87.2 AS on_time_rateSELECT
(
12.4 AS value,
-1.7 AS delta
) AS cancellation_rate,
(
87.2 AS value,
2.3 AS delta
) AS on_time_rate
SETTINGS enable_named_columns_in_function_tuple = 1{
"name": "Flight KPIs",
"favorite": true,
"view": "panel",
"panel": {
"cfg": {
"type": "kpi"
},
"fieldConfig": {
"columns": {
"cancellation_rate": {
"displayName": "Cancelled flights",
"description": "Share of flights cancelled.",
"unit": "%",
"decimals": 1,
"delta": {
"unit": " pp",
"decimals": 1,
"positiveIsGood": false
}
},
"on_time_rate": {
"displayName": "On-time flights",
"unit": "%",
"decimals": 1,
"delta": {
"unit": " pp",
"decimals": 1,
"positiveIsGood": true
}
}
}
}
}
}- zero rows:
No data; - more than one row:
Expected 1 row, got N; - tuple missing
value: unsupported KPI field; - non-numeric
value: unsupported KPI field; - unsupported fields do not prevent supported fields from rendering;
- explicit KPI with no supported fields remains a visible warning.
Compact one-row cards for values that may be numeric, textual, boolean, date-like, or composite.
A stat panel is less opinionated than KPI:
- KPI emphasizes numeric business indicators;
- stat may show labels such as
Healthy,Primary,Running, or a timestamp.
SELECT
'Primary' AS database_role,
42 AS active_queries,
now() AS last_checkedSELECT
(
42 AS value,
5 AS delta,
now() AS timestamp
) AS active_queriesProposed tuple members:
value required; scalar JSON-compatible value
delta optional numeric
previous optional scalar
timestamp optional Date/DateTime
{
"panel": {
"cfg": {
"type": "stat",
"layout": "grid"
},
"fieldConfig": {
"columns": {
"database_role": {
"displayName": "Role",
"description": "Current replication role."
},
"active_queries": {
"displayName": "Active queries",
"decimals": 0,
"delta": {
"positiveIsGood": false
}
},
"last_checked": {
"displayName": "Last checked"
}
}
}
}
}A stat field may be any supported primitive. Composite members must still satisfy their defined types. Unsupported nested structures are ignored with a warning.
Show a current value relative to a range, optional target, and visual thresholds.
SQL:
SELECT 68.2 AS cpu_usageSpec:
{
"panel": {
"cfg": {
"type": "gauge"
},
"fieldConfig": {
"columns": {
"cpu_usage": {
"displayName": "CPU",
"unit": "%",
"decimals": 1,
"gauge": {
"min": 0,
"max": 100,
"shape": "radial",
"showTarget": false
},
"thresholds": [
{ "value": 0, "color": "ok" },
{ "value": 70, "color": "warning" },
{ "value": 90, "color": "critical" }
]
}
}
}
}
}SQL:
SELECT
(
used_bytes AS value,
0 AS min,
capacity_bytes AS max,
warning_bytes AS target
) AS disk_usage
FROM ...Tuple contract:
Tuple(
value numeric,
min numeric optional,
max numeric optional,
target Nullable(numeric) optional
)
Spec:
{
"panel": {
"cfg": {
"type": "gauge"
},
"fieldConfig": {
"columns": {
"disk_usage": {
"displayName": "Disk usage",
"unit": " B",
"decimals": 0,
"gauge": {
"shape": "bar",
"showTarget": true
}
}
}
}
}
}Do not provide both runtime tuple min/max and static Spec gauge.min/max. Semantic validation should reject the ambiguity.
- missing range: use a documented panel default or report a configuration error;
min >= max: error;- value outside range: render clamped geometry but preserve/display the real value;
- target outside range: warning;
- non-numeric members: error for that field.
Render OHLC financial or operational ranges over time.
Many rows:
time column
one or more named tuple series columns
Each series tuple:
Tuple(
open numeric,
high numeric,
low numeric,
close numeric,
volume Nullable(numeric) optional
)
SELECT
toStartOfHour(ts) AS bucket,
(
argMin(price, ts) AS open,
max(price) AS high,
min(price) AS low,
argMax(price, ts) AS close,
sum(volume) AS volume
) AS price
FROM trades
GROUP BY bucket
ORDER BY bucket{
"panel": {
"cfg": {
"type": "candlestick",
"time": "bucket",
"series": ["price"]
},
"fieldConfig": {
"columns": {
"price": {
"displayName": "Market price",
"unit": " USD",
"decimals": 2,
"candlestick": {
"upColor": "ok",
"downColor": "critical",
"showVolume": true
}
}
}
}
}
}For each candle:
low <= open <= high
low <= close <= high
Violation is a runtime data error or warning according to implementation policy.
- missing time column: error;
- non-time-compatible axis: error;
- missing OHLC member: error;
- non-numeric OHLC member: error;
- duplicate time points: allowed only if the renderer documents aggregation; otherwise error;
- rows should be ordered by time.
Render a central estimate with lower and upper bounds over an X domain.
Tuple(
value numeric,
lower numeric,
upper numeric
)
SELECT
day,
(
forecast AS value,
forecast_lower AS lower,
forecast_upper AS upper
) AS demand
FROM daily_forecast
ORDER BY day{
"panel": {
"cfg": {
"type": "confidence",
"x": "day",
"series": ["demand"]
},
"fieldConfig": {
"columns": {
"demand": {
"displayName": "Forecast demand",
"unit": " orders",
"decimals": 0,
"confidence": {
"fillOpacity": 0.2,
"lineWidth": 2
}
}
}
}
}
}lower <= value <= upper
Rows violating the invariant should produce diagnostics and should not silently swap bounds.
A future tuple may include:
median
p10
p90
Such a change requires an explicit versioned member contract; do not infer arbitrary percentile names.
Render a five-number summary per category or time bucket.
Tuple(
min numeric,
q1 numeric,
median numeric,
q3 numeric,
max numeric,
outliers Array(numeric) optional
)
SELECT
region,
(
quantileExact(0)(latency_ms) AS min,
quantileExact(0.25)(latency_ms) AS q1,
quantileExact(0.5)(latency_ms) AS median,
quantileExact(0.75)(latency_ms) AS q3,
quantileExact(1)(latency_ms) AS max
) AS latency
FROM requests
GROUP BY region
ORDER BY region{
"panel": {
"cfg": {
"type": "box",
"category": "region",
"series": ["latency"]
},
"fieldConfig": {
"columns": {
"latency": {
"displayName": "Request latency",
"unit": " ms",
"decimals": 1,
"box": {
"orientation": "vertical",
"showOutliers": true
}
}
}
}
}
}min <= q1 <= median <= q3 <= max
The renderer must not reorder invalid values silently.
An Array(numeric) raw-sample field could support client-side quartile calculation later. It is not part of the summary-tuple contract and should be a separate explicit mode because it changes computation ownership.
Render time or numeric intervals, maintenance windows, reservations, availability periods, and other start/end objects.
Tuple(
start Date/DateTime/numeric,
end Nullable(Date/DateTime/numeric),
label String optional,
lane String optional,
value numeric optional,
status String optional
)
A NULL end represents an open-ended range.
SELECT
(
started_at AS start,
finished_at AS end,
deployment_name AS label,
environment AS lane,
status
) AS deployment
FROM deployments
ORDER BY started_at{
"panel": {
"cfg": {
"type": "range",
"items": ["deployment"]
},
"fieldConfig": {
"columns": {
"deployment": {
"displayName": "Deployments",
"description": "Production and staging deployment windows.",
"range": {
"showDuration": true,
"openEndedText": "running"
}
}
}
}
}
}- missing
start: error; end < start: error;- mixed incomparable start/end types: error;
- open-ended range: valid;
- empty label: allowed;
- lane controls grouping when present.
Render tasks with duration, progress, hierarchy, and dependencies.
Tuple(
id String,
label String,
start Date/DateTime,
end Nullable(Date/DateTime),
progress Nullable(numeric) optional,
state String optional,
parent Nullable(String) optional,
dependencies Array(String) optional
)
Each row contains one task tuple.
SELECT
(
task_id AS id,
task_name AS label,
started_at AS start,
due_at AS end,
progress_ratio AS progress,
state,
parent_task_id AS parent,
dependency_ids AS dependencies
) AS task
FROM project_tasks
ORDER BY started_at, task_id{
"panel": {
"cfg": {
"type": "gantt",
"item": "task"
},
"fieldConfig": {
"columns": {
"task": {
"displayName": "Project plan",
"gantt": {
"showProgress": true,
"showDependencies": true
}
}
}
}
}
}idmust be non-empty and unique;endmust not precedestart;progressconvention must be documented: recommended range is0..1;- parent ids and dependency ids may refer to rows that are filtered out; render as unresolved references with a warning;
- dependency cycles should be diagnosed.
Render pre-aggregated numeric buckets.
SQL owns bucket construction. The renderer does not rebucket arbitrary raw values in this contract.
Tuple(
lower numeric,
upper numeric,
count numeric
)
SELECT
(
bucket_lower AS lower,
bucket_upper AS upper,
count() AS count
) AS latency_bucket
FROM ...
GROUP BY bucket_lower, bucket_upper
ORDER BY bucket_lower{
"panel": {
"cfg": {
"type": "histogram",
"buckets": ["latency_bucket"]
},
"fieldConfig": {
"columns": {
"latency_bucket": {
"displayName": "Request latency",
"unit": " ms",
"histogram": {
"normalization": "none",
"gap": 0
}
}
}
}
}
}lower < upper;count >= 0;- buckets should be sorted by lower bound;
- overlapping buckets should produce a warning unless explicitly supported;
- gaps are valid;
- infinite outer bounds require a documented representation.
normalization is presentation behavior over returned counts:
none: show counts;percent: divide each count by total count;density: normalize by total and bucket width.
The raw SQL counts remain unchanged.
Render a two-dimensional grid of pre-aggregated cells.
Tuple(
xLower numeric/time,
xUpper numeric/time,
yLower numeric,
yUpper numeric,
value numeric
)
SELECT
(
time_bucket AS xLower,
time_bucket + INTERVAL 5 MINUTE AS xUpper,
latency_lower AS yLower,
latency_upper AS yUpper,
count() AS value
) AS cell
FROM latency_cells
ORDER BY xLower, yLower{
"panel": {
"cfg": {
"type": "heatmap",
"cells": ["cell"]
},
"fieldConfig": {
"columns": {
"cell": {
"displayName": "Latency distribution",
"unit": " requests",
"heatmap": {
"colorScale": "sequential",
"reverse": false,
"showValues": false
}
}
}
}
}
}xLower < xUpper;yLower < yUpper;- cell
valuemust be numeric; - overlapping cells should warn;
- sparse grids are valid;
- the renderer must not assume equal bucket widths.
Render nodes connected by weighted or unweighted directed/undirected edges.
Tuple(
source String,
target String,
value Nullable(numeric) optional,
sourceLabel String optional,
targetLabel String optional,
state String optional
)
Each row is one edge.
SELECT
(
caller_service AS source,
callee_service AS target,
count() AS value,
caller_service AS sourceLabel,
callee_service AS targetLabel
) AS edge
FROM service_calls
GROUP BY caller_service, callee_service{
"panel": {
"cfg": {
"type": "network",
"edge": "edge"
},
"fieldConfig": {
"columns": {
"edge": {
"displayName": "Service dependencies",
"unit": " calls",
"network": {
"directed": true,
"showLabels": true
}
}
}
}
}
}- source/target ids define node identity;
- labels are optional display text;
- repeated identical edges may be aggregated by SQL or by a documented renderer rule;
- negative weights should be rejected unless a future signed-edge mode is defined;
- self-edges may be allowed but should be visibly distinct.
A richer node table cannot be represented as a second result set in the current single-query contract. Initial implementations should derive nodes from edges. A future tuple may add node group/color information, or a separate setup/source query may supply node metadata.
Render directional flow magnitudes between stages or categories.
Tuple(
source String,
target String,
value numeric,
sourceLabel String optional,
targetLabel String optional
)
SELECT
(
source_stage AS source,
target_stage AS target,
sum(flow_count) AS value,
source_stage AS sourceLabel,
target_stage AS targetLabel
) AS flow
FROM stage_flows
GROUP BY source_stage, target_stage{
"panel": {
"cfg": {
"type": "sankey",
"edge": "flow"
},
"fieldConfig": {
"columns": {
"flow": {
"displayName": "Request flow",
"unit": " requests",
"sankey": {
"nodeWidth": 16,
"nodeGap": 10
}
}
}
}
}
}value >= 0;- empty source or target: error;
- cycles require a renderer that supports them; otherwise diagnose;
- duplicate flows should be aggregated in SQL or by an explicit documented rule;
- source and target ids form the node set.
Render one or more operational health objects with state, severity, message, and age.
SELECT
(
'healthy' AS state,
'ok' AS severity,
'Replication is current' AS message,
now() - INTERVAL 2 MINUTE AS since
) AS replication,
(
'degraded' AS state,
'warning' AS severity,
'Two disks above 80%' AS message,
now() - INTERVAL 15 MINUTE AS since
) AS storageTuple contract:
Tuple(
state String,
severity String,
message String optional,
since Date/DateTime optional,
value scalar optional
)
{
"panel": {
"cfg": {
"type": "status",
"layout": "cards"
},
"fieldConfig": {
"columns": {
"replication": {
"displayName": "Replication",
"status": {
"showMessage": true,
"showSince": true,
"severityColors": {
"ok": "ok",
"warning": "warning",
"critical": "critical",
"unknown": "muted"
}
}
},
"storage": {
"displayName": "Storage",
"status": {
"showMessage": true,
"showSince": true
}
}
}
}
}
}For a variable number of checks, return one status tuple per row and configure the tuple column explicitly:
SELECT
(
check_id AS id,
check_name AS label,
state,
severity,
message,
changed_at AS since
) AS check
FROM health_checks
ORDER BY severity DESC, check_nameA future row-list status contract should require cfg.items: ["check"] or a dedicated item field. It must not be inferred ambiguously from arbitrary tuples.
Recommended canonical severities:
ok
info
warning
critical
unknown
Unknown severity strings should render with the unknown style and a warning rather than disappearing.
- the number of visual objects is fixed and small;
- the query returns one row;
- each object is naturally named;
- the panel should preserve SQL column order.
Typical: KPI, stat, gauge, status cards.
- the number of objects varies;
- each object shares the same schema;
- sorting/filtering objects in SQL is useful;
- the panel naturally consumes a sequence.
Typical: candles, ranges, tasks, buckets, cells, edges.
- multiple values jointly define one object;
- member names have stable semantics;
- values must travel together;
- positional interpretation would be fragile.
- one row must contain a variable-size nested collection;
- row-oriented output is impractical;
- the panel parser explicitly supports the nested array.
Rows are usually easier to stream, inspect in the Table view, export, limit, and diagnose.
Before producing a Spec:
- Identify the panel type.
- Decide whether it is one-row or row-oriented.
- Keep calculations in SQL.
- Use one top-level column per fixed visual object.
- Use a named tuple when an object needs several runtime values.
- Use exact documented tuple member names.
- Keep display labels, descriptions, units, decimals, colors, and thresholds in Spec.
- Key field metadata by exact top-level result-column name.
- Add
ORDER BYwhen row order matters. - Use nullable members for optional runtime values.
- Do not add an authored trailing
FORMAT. - Do not put SQL, ids, or runtime results inside Spec.
- Preserve unknown fields.
- Validate JSON structure before Save.
- Run result-aware validation against actual column names/types and row shape.
Examples:
- invalid JSON;
- unknown implemented panel cfg shape;
- wrong JSON property type;
- user-authored trailing
FORMATfor a panel-owned transport; - missing required tuple member;
- wrong required member type;
- invalid row count for explicit one-row panel;
- invalid cross-member invariant such as
min >= max.
Examples:
- field metadata targets a column not in the current result;
- unknown tuple member;
- target outside gauge range;
- overlapping histogram buckets;
- unresolved Gantt dependency;
- unknown status severity.
Examples:
- optional presentation metadata absent;
- auto-detected field label uses raw SQL column name;
- neutral delta coloring used because
positiveIsGoodis absent.
The reader must follow this pattern:
clone complete Spec
validate known fields
read known fields
preserve unknown fields
persist complete Spec
Do not rebuild panel, fieldConfig, or column metadata from a whitelist during Save, rename, favorite, direct panel edits, import/export, share, or merge.
A build that does not implement a future panel type should preserve it and render a clear unsupported-type diagnostic.
- ClickHouse Tuple data type: https://clickhouse.com/docs/sql-reference/data-types/tuple
- ClickHouse 24.7 named-tuple aliases: https://clickhouse.com/blog/clickhouse-release-24-07
- JSONEachRowWithProgress: https://clickhouse.com/docs/interfaces/formats/JSONEachRowWithProgress
- Named tuples as JSON objects: https://clickhouse.com/docs/operations/settings/formats#output_format_json_named_tuples_as_objects
- JSON Schema 2020-12: https://json-schema.org/draft/2020-12/
- CodeMirror autocompletion reference: https://codemirror.net/docs/ref/#autocomplete
Use dashboard.role: "filter" on a favorited query to replace matching
Dashboard parameter fields with strict curated option controls. The query must
return one row; each top-level result-column name is the exact target parameter
name. Supported values are Array(T),
Array(Tuple(value T, label L)), and Map(K,V). Arrays keep source order;
Maps sort by label and then value. Filter is never a panel.cfg.type, creates no
tile, preserves any dormant panel object, and does not persist its workbench
preview as spec.view.