Declaring a chart
Load this card when a task adds or changes chart metadata on a Response or
SubscriptionData payload: a mark, channels, scales, guides, or the way a
chart-producing request appears in the endpoint shell. It holds the
two-dimensional rules and the rules every chart shares; a 3D chart also loads
the 3D chart card after this one. Its rules are generated from
the 2D chart specification, the
chart summary, the
2D chart annotation rows
and the endpoint presentation contract,
which own them; the
schema chart inventory
summarizes them. Chart appearance hints (scale, guide, palette, chart)
are template members on the Display Template card.
Renderer rules (layout, ticks, legends, overlays, hit testing, accessibility
trees) are on the client runtime card; an App author
relies on their result but never encodes it.
Order of work
- Put the chart on output data: a unit List of row Tables, a carrier Table,
a composition or a Union wrapper (
chart.data.*,chart.compose.*,annot.chart.*), returning a complete chart-ready snapshot computed by the App (chart.snapshot.*,chart.exclusion.*). - Choose the mark by geometry and bind direct scalar row fields to channels
(
chart.mark.*,chart.channel.*,annot.chart-channel.*,chart.geometry.*,chart.order.*,chart.row.*). - Give every field its semantic type, unit and format (
chart.type.*,chart.unit.*,chart.format.*), and keep every text value typed (chart.type.source-*,chart.l10n.*). - Declare scales only to override or share them (
chart.scale.*,annot.chart-scale.*,chart.domain.*,chart.lane.*,chart.shared-scale.*,money.shared-scales-match), and guides only for an explicit override or a value/span label (chart.guide.*,annot.chart-guide.*,chart.guide-label.*,chart.legend.*,chart.explicit-guides). - Leave appearance to the template (
chart.appearance.*,chart.palette.*,chart.appearance-in-template) and keep the structured view meaningful (chart.structured-view-meaningful). - Design interaction, fallback and limits around the client contract
(
chart.interaction.*,chart.fallback.*,chart.limits.*,chart.a11y.*,chart.attr.*,chart.example.*). - Know how the endpoint presents chart-producing requests and their previews
(
endpoint.presentation.*,endpoint.workspace.*,chart.preview.*). - Compile with the Studio MCP
compile_schematool and fix every reported rule.
Rules to apply
Studio does not check these rules. Each is quoted from its owner.
1. Purpose
Owner: 2d.md#1-purpose
chart.data.app-computes: The App task remains responsible for all domain calculations—including arbitrary time transforms—and can return an arbitrarily transformed but strictly typed payload. The client is responsible only for scales, layout, primitive marks, guides, accessibility, and standard chart interaction.chart.data.scope-limits: These attributes target broad chart-family coverage for server-prepared data. They do not provide Vega-Lite syntax compatibility, pixel compatibility, or complete feature parity. In particular, they do not reproduce Vega's runtime language, signals, arbitrary event expressions, client-side transforms, geographic coordinate/projection/topology semantics, raster/image marks, or a general widget system. A server-projected geographic region is ordinary polygon geometry and carries no client-side geographic meaning.
2. Three distinct attribute layers
Owner: 2d.md#2-three-distinct-attribute-layers
chart.layers.separate-responsibilities: The word "attribute" refers to three related but different things in Libertas. Chart support must keep them separate.
2.4 Client interpretation
Owner: 2d.md#24-client-interpretation
chart.data.atomic: Chart presentation is atomic: a consumer either validates the complete current contract or presents the existing ordinary data view.
3.1 Chart metadata belongs to returned data
Owner: 2d.md#31-chart-metadata-belongs-to-returned-data
chart.data.hidden-row-allowed: A row field may carryHidden=true. It is chart data, not presentation: the client keeps it in the retained row value, so a custom chart request can CopyFrom it.chart.data.carrier-context: Hidden carrier fields may use any otherwise valid Libertas type and remain ordinary encoded response data. They do not become rows, channels, guides, titles, tooltips, accessibility content, interaction identities, or chart resource usage. Their primary purpose is request/response transaction context analogous to an HTTP cookie: the client retains the complete original response tree so a later typedNextRequestorCopyFromoperation can copy the context back without displaying it or requiring the App to duplicate it in every chart row.chart.data.title-from-variant: For a protocol chart, the selectedResponseorSubscriptionDatavariant's localized name and description supply the overall chart title and description, including when its chart payload is a child field. Unwrapping the payload does not replace that wording with the child field's documentation. Charts nested inside a composition retain their own localized names and descriptions.
3.2 Complete snapshots
Owner: 2d.md#32-complete-snapshots
chart.snapshot.replace: Every response, and every complete subscription snapshot, replaces the previous chart snapshot. A subscription report may instead update the working value through the shared SubscriptionData mechanisms: an accepted append report extends the chart-bearing working array, and a reduced SubscriptionData report updates the stable value it copies to. The chart presents the complete resulting value. The client does not otherwise join, patch, or merge chart rows. An empty list is a valid empty chart when the schema is valid.chart.snapshot.no-new-message: Existing protocol response and subscription routing is unchanged: the normal response reaches the originating client, and chart metadata arrives with the App schema rather than in a new Hub/client message or in every payload.chart.snapshot.request-new-snapshot: If a chart must react to a user choice, the client sends a typed Libertas request and the endpoint returns another complete snapshot. Filtering, aggregation, binning, sorting, stacking, window calculation, regression, projection, and sampling therefore remain ordinary App code.
3.4 The source tree is authoritative
Owner: 2d.md#34-the-source-tree-is-authoritative
chart.data.source-tree: Clients traverse the decodedScriptValuetree, including the selected concrete Union variant. They must not build chart data from Task Editor visible rows. For carrier tables they project only the contract-selected chart child while retaining the unmodified root tree as the request context.Hiddenremoves a carrier field from chart projection even when its referenced effective type also carriesChart; it does not remove, redact, re-encode, or detach that value from the response. Features such as the Display Template'spromoteandflattencan promote, flatten, or hide presentation rows but do not change the underlying chart shape.
4.1 Primitive marks
Owner: 2d.md#41-primitive-marks
-
chart.mark.shared-channels:row,column,tooltip,detail,order, andkeyare available to every mark under their ordinary facet, grouping, ordering, and interaction rules. Cartesian offsets remain subject to the discrete parent-band rules in §5.3. Appearance applicability is closed:Mark Allowed appearance channels pointcolor,fill,stroke,opacity,fillOpacity,strokeOpacity,size,shape,angle,strokeWidth,strokeDashlinecolor,stroke,opacity,strokeOpacity,strokeWidth,strokeDasharea,polygon,bar,rect,arccolor,fill,stroke,opacity,fillOpacity,strokeOpacity,strokeWidth,strokeDashrulecolor,stroke,opacity,strokeOpacity,strokeWidth,strokeDashtextcolor,fill,opacity,fillOpacity,size,angle -
chart.mark.tick-as-rule(judgment): Tick-like glyphs use an explicit shortrule, so their length is portable data-space geometry rather than a platform-dependent size. Point, line, rule, and text also have the polar coordinate forms defined in Section 5.3.polygonsupplies the closed filled-path primitive;lineremains open andarcremains the only primitive whose edges are circular rather than straight screen-space segments. -
chart.mark.family-histogram(judgment): histogram: server-computed bins rendered asbarwithx,x2,y; -
chart.mark.family-stacked(judgment): stacked bar or area: server-computed bounds rendered withy,y2orx,x2; -
chart.mark.family-box-plot(judgment): box plot: layeredrectandrule; -
chart.mark.family-candlestick(judgment): candlestick: layeredrectandrule; -
chart.mark.family-ohlc(judgment): OHLC: layeredrulemarks; -
chart.mark.family-error-band(judgment): error bar or confidence band: layeredruleorareaandline; -
chart.mark.family-map-network(judgment): map, network, or Sankey: server-computed coordinates rendered with primitive polygons, paths, rules, areas, points, and text.
4.2 Compositions
Owner: 2d.md#42-compositions
-
chart.compose.layer-order:Value Meaning layerDraw every child in schema order in one plot area. Later children are visually above earlier children. -
chart.compose.facets-and-grids(judgment): Faceting is expressed byrowandcolumnchannels on a unit chart. A repeat operation is unnecessary: the endpoint emits the explicit chart children or facet values it wants the client to display. A grid is represented by nestedhconcatandvconcatcompositions, avoiding a separate column-count grammar.
5. ChartChannel
Owner: 2d.md#5-chartchannel
chart.channel.canonical-order: Studio's shared validator deduplicates roles and writes them in the canonical order shown below.chart.channel.nearest-complete: The nearestChartChanneldeclaration is complete; channel values are not merged through a reference chain.
5.3 Cartesian and polar geometry
Owner: 2d.md#53-cartesian-and-polar-geometry
chart.geometry.endpoint-order:xandylocate the primary point.x2andy2are explicit data-space endpoints. Endpoint order is not semantic: clients render the interval between the two mapped values, soopen > closeand reversed ranges are valid. A zero-length rectangle dimension remains meaningful and is drawn one device pixel thick, so a doji or median remains visible.chart.geometry.area-baseline: withouty2, a verticalareahas a quantitative y baseline of zero;chart.geometry.rect-no-baseline:rectnever invents a baseline;chart.geometry.polar-zero: angular zero is at twelve o'clock and positive angles advance clockwise;chart.geometry.theta-turns: a linear theta domain maps its minimum to zero turns and maximum to one full turn; an identity theta value is already measured in turns;chart.geometry.radius-normalized: an identity radius is normalized from0at the plot center to1at the available plot radius; a quantitative radius scale maps its domain into the same normalized interval;chart.geometry.polar-range: mapped theta and radius values must be finite and lie in the inclusive interval0...1. An out-of-range mapped coordinate is invalid; it is not wrapped or clamped to the circular boundary. An invalid point, rule, or text row is suppressed; an invalid line vertex breaks its path; and an invalid polygon vertex becomes a subpath separator after scale domains are resolved. Its finite raw positions still contribute to automatic domain resolution, but it contributes no geometry, appearance domain, group-style comparison, hit target, or accessibility focus. Tooltip and accessibility output for valid geometry retain the raw source value, not its normalized coordinate;chart.geometry.straight-segments:point,line,rule,text, andpolygonuse straight screen-space segments after the conversion above. A provider samples any spiral, circular edge, geodesic, or other curve it needs. Onlyarchas intrinsically circular edges;chart.geometry.arc-angular-order: before output-range reversal,theta2must not precedetheta, and one arc cannot exceed one full turn;chart.geometry.arc-default-radius: omitting both radial channels means inner radius0and outer radius1;chart.geometry.arc-radial-order: before output-range reversal,radiusis the inner source endpoint andradius2is the outer source endpoint.chart.geometry.arc-mapped-interval: After both endpoints are mapped through their shared scale, painted arc geometry and hit testing use the interval between the two mapped endpoints.reverse=truemay therefore place the mapped primary endpoint above its secondary endpoint without invalidating the row: the physical angular or radial interval is mirrored, while source roles and tooltip values remain unchanged. Both mapped endpoint intervals must remain within0...1; polar geometry is not wrapped or clipped across an angular boundary. A source ordering or mapped-range violation suppresses that arc row as invalid geometry; it is not repaired by reordering the source values.chart.geometry.polar-closure(judgment): For connected polar vertices, mapped theta0and1are physically coincident. Polygon closure is implicit, so authors normally use an explicit theta domain such as0...1, emit vertices in the half-open interval0...1, and do not repeat the first vertex. Repeating it is nevertheless well-defined: it remains a counted source vertex and creates a zero-length edge. An automatic theta domain still maps its finite minimum to0and maximum to1; the client does not invent a cyclic category or an unobserved angular endpoint.
5.4 Default order and grouping
Owner: 2d.md#54-default-order-and-grouping
-
chart.order.grouping: List order is the default path and semantic paint order. Anorderchannel overrides it. The sole presentation-only exception is the synthetic-overlap z-order rule in Cartesian and polar geometry, which never reverses two rows whose authored raw intervals overlap. For line, area, and polygon marks, rows are first partitioned by facet values and then by the compositedetailkey. Acolor,stroke, orfillcategory does not implicitly become a grouping key; the same field should also declaredetailwhen separate paths or polygons are required:TypeScriptenum TemperatureSeries { Indoor, Outdoor, } /** @LibertasChartChannel(color, detail, tooltip) */ series: TemperatureSeries; -
chart.order.polygon-subpaths: Within one polygon facet/detail group, source order first determines subpaths. A row with a null or non-finite required position is a source separator. After scale domains are resolved, a polar row whose finite required positions map to non-finite values or outside0...1is a mapped separator. Either kind ends the current subpath and begins a new one at the next validly mapped vertex. Leading, trailing, and consecutive separators do not create empty geometry. Iforderis present, the client then sorts the validly mapped vertices ascending within each resulting subpath, retaining source order for equal values; sorting never moves a vertex across a separator. For polygon,orderaffects only vertices within a subpath. Polygon groups paint in order of their first validly mapped source row and are not reordered by one member'sordervalue. -
chart.order.polygon-evenodd: Each contiguous run of at least three finite mapped vertices is closed by an implicit last-to-first edge. A shorter run produces no polygon geometry and a bounded diagnostic. All valid subpaths in one facet/detail group form one compound polygon. The fill rule is alwaysevenOdd: a test point is filled when a ray from it crosses the compound boundary an odd number of times. Ring winding has no meaning, nested rings create holes by parity, and disjoint rings create islands. Self-intersections, coincident vertices, touching rings, and crossing subpaths are legal and use the same rule; clients neither repair them nor impose a platform-native simple-polygon restriction. This permits, among other valid data, a radar polygon whose zero values collapse several vertices at the center. -
chart.order.polygon-edges: Polygon edges are straight segments in mapped screen coordinates. Their implicit closing edges participate in stroke dashing, clipping, hit testing, and resource accounting. A repeated first vertex is permitted but unnecessary; it remains a source vertex and creates one zero-length edge rather than changing closure or fill semantics. A provider samples a curved boundary explicitly. -
chart.row.key-identity: The optionalkeychannel identifies a logical row across complete snapshots. It supports stable accessibility focus and future transitions, but does not change snapshot replacement semantics. -
chart.row.no-implicit-key: Position, tooltip, and detail values are not implicit row keys. Any number of source rows may carry the samex,y, temporal bucket, or other scalar value; clients preserve those rows independently through projection, paint, tooltip, and accessibility lowering. This rule is type-independent: a repeated bucket may beDateOnly,DateTime, numeric, or categorical. Only an authoredkeyasserts logical row identity, and its documented facet/detail scope still applies. -
chart.type.raw-identity: Categorical identity is always the typed raw value, never its rendered label. Enumeration members use their member value.Device,VirtualDevice,User,Endpoint, andLanDeviceuse their Libertas reference ID. FormattedText Bytes use their canonical tuple blob. Resolved names or localized text may be duplicated or changed without merging categories, changing focus identity, or reordering a domain. -
chart.order.enum-appearance-domain: An Enumeration is a closed categorical declaration. For an automatic categoricalcolor,fill,stroke,shape, orstrokeDashscale, the domain contains every declared member in declaration order, including members that have no eligible row in the current snapshot. This keeps appearance slots and legends stable as sparse data appears or disappears; declared-but-absent members create no marks. The complete declared appearance domain is subject to the ordinary paint, shape, and dash category limits. Position, offset, and facet domains remain derived from eligible rows, so a declaration alone never creates an empty axis band, nested lane, or facet pane. Open categorical types likewise remain row-derived.
5.5 Type inference
Owner: 2d.md#55-type-inference
-
chart.type.datetime-local:Libertas type Default chart interpretation DateTimeTemporal data shown in local time unless type=utcis explicit. -
chart.type.string-row:Libertas type Default chart interpretation StringGenuine literal runtime/domain text, a nominal category whose value is itself literal data, or a literal appearance token. It is never a fixed program vocabulary or a copied object display name. -
chart.type.string-source-choice: TheStringrow is a semantic assertion about provenance, not permission to put untranslated program wording in a payload. Whenever a channel can expose a value as human-readable text—including position/facet labels, ordinal legends,text,tooltip, explicit value/span guides, and accessibility—the App must choose the source type as follows: -
chart.type.source-enumeration: a closed App-defined vocabulary uses anEnumeration; -
chart.type.source-object: an identity that is a Libertas system object remainsDevice,VirtualDevice,User,Endpoint, orLanDevice, so the ordinary client resolver supplies its display name; -
chart.type.source-formatted: App-authored dynamic or parameterized wording uses a formatted Bytes value and LMF1; and -
chart.type.source-literal: only genuine literal domain/user text, such as an externally supplied proper name or free-form annotation, remains a plainStringand is displayed verbatim. -
chart.type.string-runtime-token(judgment): A small or repeated set of proper names does not become a program vocabulary, and a token does not become literal data merely because task code writes it at runtime. A plain String used only bykey, or by an identity-scale appearance channel whose guide is necessarily absent, is not rendered as text and is outside this presentation-source rule. If that same field also carriestooltipor another text-emitting role, the rule applies. -
chart.type.string-provenance-review: The schema validator can enforce the effective scalar, channel, scale, and guide combinations, but schema JSON cannot prove whether task code obtained a String from a user/domain source or selected it from a fixed English token set. Declaring a visible plain String is therefore an author/provider conformance assertion that package review and response fixtures must audit. -
chart.type.date-only-civil:DateTimeis an instant. An ordinaryNumberretains its declared quantitative semantics, including unit or duration metadata.DateOnly=trueis the explicit exception: its integer is the decimal calendar tupleyyyyMMdd, not epoch time. Clients validate the Gregorian date and construct chart/calendar components directly, without local- or UTC-offset conversion. -
chart.type.timeonly-range: ATimeOnlypayload is finite seconds from midnight in0 <= value < 86400. An out-of-range value is invalid typed input and never wraps modulo a day for geometry, grouping, labels, or tooltips.
5.5.1 Physical units
Owner: 2d.md#551-physical-units
chart.unit.identifier-meaning: CLDR core identifiers include simple units such asmillimeter, generated compound units such asmillimeter-per-day, products such askilowatt-hour, and powered units such assquare-meter. These identifiers are not source field names, localized strings, symbols, legacy UCUM aliases, or App resource keys. Thus an author declaresmillimeter, while a supporting CLDR formatter may visibly producemmor a localized long name.percentmeans that raw50is fifty percent and displays as50%; it never rescales0.5into fifty percent. A plain Number withPhysicalUnit=secondis a measured quantity in seconds, whileTimeIntervalretains its separate duration semantics; the two attributes cannot coexist.chart.unit.chart-surfaces: Within a chart, the rule applies wherever that exact numeric field is rendered: ordinary and explicit tooltip values, Cartesian and polar scale-guide ticks, numericsource=valueorsource=spanguide labels, continuous appearance legend values, numeric text marks, selection/guide overlays, and accessibility. Asource=scaleguide uses its target binding's unit; a value/span guide uses the declaring label field's unit and never borrows the target's unit. Field and guide titles remain their independently localized document text and do not silently gain a unit suffix. Null or suppressed values produce no standalone unit text.
5.5.2 Scalar display formats
Owner: 2d.md#552-scalar-display-formats
chart.format.text-only: A chart applies the effectiveFormatwhenever that field produces visible text: scale ticks, tooltips, text marks, value/span guide labels, selection overlays, and accessibility descriptions. It never parses the formatted text back into data. Domains, tick candidates, sorting, grouping, equality, focus keys, interpolation, scale transforms, and geometry always use the raw Number,yyyyMMdd, epoch-second, or seconds-from-midnight value. DateTime formatting uses the viewer's local timezone even when an explicitlyutcscale uses UTC for geometry; the authored pattern controls presentation, not scale semantics.chart.format.datetime-range: A chartDateTimepayload is an integral Unix second in the inclusive range0..253402214399, whose upper endpoint is 9999-12-30T23:59:59Z. This chart-specific bound deliberately reserves a full day before the end of civil year 9999 so conversion through any configured time-zone offset whose absolute value is less than 86400 seconds stays within the proleptic-Gregorian years 0001 through 9999. A nonintegral or out-of-range row value, explicit bound, unavailable time-zone offset, or conversion outside that civil range invalidates chart presentation with a bounded diagnostic; it is never clamped or delegated to a platform formatter's private range. Client-generated temporal ticks and transient DateTime viewport edges may be fractional epoch seconds; they are inert derived guide instants, never a DateTime payload, tooltip, key, or typed request value.chart.type.retain-domain-identity: An App must not flatten anEnumerationor Libertas object reference into a duplicate displayStringmerely for a chart. Keeping the typed value lets the frontend apply the same localization and permission-aware identity resolution as every other Libertas view, without adding configuration burden to the end user.chart.row.nullable-positions: Nullable positional values create missing points. For line and area marks, a missing position breaks the path; it is not interpolated. For polygon, it separates subpaths as defined in Section 5.4. A missing optional appearance value uses the native mark default. Non-finite numbers receive the same mark-specific suppression or separator behavior and are reported through bounded diagnostics.
5.6 Appearance values
Owner: 2d.md#56-appearance-values
-
chart.appearance.defaults: Without an appearance channel, the client uses its accessible theme-native default. Categorical paint channels use a deterministic Libertas palette. Literal values useChartScale(type=identity); their implicit guide is none. -
chart.appearance.color-default-paint:coloris the mark's default paint: it supplies stroke forlineandrule, fill forarea,bar,rect,arc,text, andpolygon, and both fill and stroke forpoint. An explicitfillorstrokeoverridescolorfor that component. Overallopacitymultiplies the color alpha and the applicablefillOpacityorstrokeOpacity; missing opacity components default to1. -
chart.appearance.explicit-stroke: An explicitstrokepaints the boundary of an area, bar, rect, arc, polygon, or point and the path of a line or rule. For area, bar, rect, arc, and polygon, the boundary is absent when no explicit stroke resolves;strokeWidth,strokeDash, andstrokeOpacityalone never synthesize it. Text uses its fill paint for glyphs and has no outline in version one. Point shape, rotation, radius, fill, and boundary use the same final geometry in marks, legends, hit testing, and accessibility. -
chart.appearance.polygon-fill: A polygon always has a fill: an explicitfill, otherwisecolor, otherwise the accessible theme-native polygon fill. Its boundary stroke is absent unless an explicitstrokebinding resolves to visible paint.strokeWidth,strokeDash, andstrokeOpacitydo not synthesize a stroke or a stroke hit region by themselves. -
chart.appearance.polygon-group-style: A polygon facet/detail group has one paint and stroke style, not a per-vertex gradient. For every group-level appearance binding—color,fill,stroke,opacity,fillOpacity,strokeOpacity,strokeWidth, andstrokeDash—all finite, validly mapped vertices in all of that group's subpaths must carry the same typed canonical raw value, or all carry null and use the same mark default. Comparison uses raw identity, never a localized label, clamped scale output, or theme-adjusted paint. A mismatch invalidates the complete chart presentation and invokes the chart failure policy in §10. -
chart.appearance.discrete-order: For any discrete scale, Enumeration values use declaration order; Boolean usesfalse,true; Number and Libertas object references use raw numeric order; a discrete DateTime uses raw epoch-second order and a DateOnly value uses its validated chronologicalyyyyMMddnumeric order; String uses Unicode scalar-value order of its raw value; and formatted Bytes use canonical blob order. Resolved display names and formatted output never affect ordering. The same ordering applies to categorical axes, legends, facets, and nested bands. An automatic categorical paint domain is limited to ten values and maps in order to these stable sRGB base hues:Text#4E79A7,#F28E2B,#E15759,#76B7B2,#59A14F, #EDC948,#B07AA1,#FF9DA7,#9C755F,#BAB0AC -
chart.palette.identity-contrast(judgment): An identity color is literal endpoint data and is never theme-adjusted. An author who choosesChartScale(type=identity)owns its contrast on every supported theme. -
chart.palette.ten-categories: An endpoint needing more than ten automatic paint categories supplies a bounded identity-color field or uses facets; clients do not repeat colors and imply false equivalence. Positional bands, facet values, and textual legends are not subject to the paint limit; they remain subject to row, facet, and layout caps. -
chart.palette.diverging: Withmid, it uses these blue-neutral-red stops with the neutral stop fixed at the midpoint:Text#053061,#2166AC,#4393C3,#92C5DE,#D1E5F0,#F7F7F7, #FDDBC7,#F4A582,#D6604D,#B2182B,#67001F -
chart.palette.custom-colors(judgment): Authors needing different colors send validated identity values. -
chart.appearance.ranges-and-limits: Other automatic appearance ranges are also portable: point radius maps to3..18density-independent points, text size to10..32, stroke width to1..8, opacity to0.15..1, and angle to0..360clockwise degrees. Ordinal shape usescircle,square,triangle,diamond,crossin order, while ordinal stroke dash usessolid,dash,dot,dash-dot. Automatic shape and dash domains are therefore limited to five and four categories respectively. Identity values are used directly. Size must be finite and at least0.5, with no schema upper bound; stroke width is bounded to0..32, opacity to0..1, and angle to finite degrees. -
chart.appearance.identity-color:Channel Identity value color,fill,strokesRGB #RRGGBBor#RRGGBBAA. -
chart.appearance.identity-shape:Channel Identity value shapeA supported token such as circle,square,triangle,diamond, orcross. -
chart.appearance.identity-dash:Channel Identity value strokeDashsolid,dash,dot,dash-dot, or a bounded space-separated positive-number pattern. -
chart.appearance.unsupported-literal: Unsupported literal values invalidate the chart presentation, not the protocol payload. Arbitrary CSS, URLs, scripts, and platform-specific color objects are not accepted.
5.6.1 Display Template palette
Owner: 2d.md#561-display-template-palette
-
chart.palette.hint: A Display Templatepalettehint on aChartorChart3Droot selects the discrete color list for that chart's automatic categorical and ordinal paint (color,fill, andstroke) and for the default colors of series that bind no paint channel:Value Discrete color list autoThe specification default: the ten categorical base hues above. categoricalThe same ten categorical base hues. sequentialThe ten Viridis stops above, as entries 0through9.divergingThe eleven blue-neutral-red stops above, as entries 0through10. -
chart.palette.hint-scope: Categoryiuses entryiof the selected list, the theme contrast adjustment applies to every list, and the ten-category paint limit applies for every value. The hint never changes continuous numeric paint, whose stopsmidalone chooses, or identity colors.
5.7 Runtime row semantics
Owner: 2d.md#57-runtime-row-semantics
chart.row.order-sort: List order is stable source order for semantic paint order and hit-test ties. Anorderbinding sorts ascending by the effective scalar's canonical ordering; equal values retain list order. The synthetic-overlap exception in Cartesian and polar geometry changes only screen z-order and the matching hit-test tie for otherwise-disjoint intervals; canonical order remains the owner of style fallback and every raw-overlap tie.chart.row.unordered-list-order:Unorderedon a chart list does not change chart geometry. A line, area, or polygon without anorderbinding follows the received list order, as on any other list, and polygon separator rows split subpaths in that order. An App whose path must not depend on the emitted sequence declaresorder.chart.row.null-required: A null required position suppresses a point, bar, rect, rule, arc, or text row. For line and area, a null required position breaks the path. For polygon it is a subpath separator under Section 5.4. A non-finite required position has the same mark-specific behavior and produces a bounded diagnostic; it never reaches a native drawing API. Null optional style values use the mark default. Tooltip fields are shown in structural field order and null tooltip values are omitted.chart.row.key-unique: A non-nullkeymust be unique within the tuple of chart unit, facet values, anddetailvalues. A duplicate key makes the runtime chart invalid and invokes the chart failure policy in §10 rather than ambiguous interaction. Withoutkey, row index is identity only for the current complete snapshot.
6. ChartScale
Owner: 2d.md#6-chartscale
-
chart.scale.optional:ChartScaleis optional. Native automatic scales are used when it is absent. Itstypereaches clients through the Display Template as described above. Its generatedValueis a comma-separated sequence ofkey=valueproperties in this canonical order:Textid,type,min,max,mid,zero,reverse
6.1 Properties
Owner: 2d.md#61-properties
chart.scale.nearest-replaces: Because lookup uses nearest-value precedence, a localChartScalereplaces the complete inherited or referenced scale value; individual scale properties are never merged across declarations.chart.scale.time-vs-utc: ForDateTime,timeformats and groups an epoch value in the device's configured local time zone andutcuses UTC. ForDateOnly, both scale tokens operate on the same timezone-free calendar components.identityis intended for literal appearance values and normalized polar geometry; it must not bypass bounds or type validation.chart.scale.explicit-domain-clips: Explicit bounds and automatic-domain inference remain raw data values and are validated before transformation. Values outside an explicit domain are clipped by the plot; a client does not clamp or rewrite their source values. Whenlo == hi, that one transformed value maps to the range midpoint and the scale-sourced guide exposes one raw-value tick; clients do not invent different padding domains around it.chart.scale.raw-values-exposed: Tooltips, accessibility, explicit bounds, and request values always expose raw values, never transformed coordinates. The logarithm base does not change a normalized position; natural logarithms above define the portable inverse.chart.scale.discrete-reverse: For ordinal, band, and point scales,reverse=truereverses the output range, not the canonical raw-identity domain. Axis/facet placement and automatic appearance ranges therefore run in the opposite direction while grouping, ordering, keys, labels, and tooltip identity remain unchanged.
6.2 Automatic domains
Owner: 2d.md#62-automatic-domains
-
chart.domain.default-number:Field and channel Default scale Numberon a quantitative channellinear -
chart.domain.default-datetime:Field and channel Default scale DateTimeon a positiontime -
chart.domain.default-date-only:Field and channel Default scale NumberwithDateOnlyon a positiontime, formatted as a timezone-free calendar date -
chart.domain.default-time-only:Field and channel Default scale TimeOnlyon a positionlinear, formatted as local clock time -
chart.domain.default-time-interval:Field and channel Default scale NumberwithTimeIntervallinear, formatted as a duration -
chart.domain.default-discrete-band:Field and channel Default scale Discrete value on bar,rect,row,column, or an offsetband -
chart.domain.default-discrete-point:Field and channel Default scale Discrete value on another position point -
chart.domain.default-discrete-ordinal:Field and channel Default scale Discrete value on paint, shape, or stroke dash ordinal -
chart.domain.default-numeric-paint:Field and channel Default scale Numeric paint linearsequential, or divergent whenmidis present -
chart.domain.datetime-utc-choice(judgment): ADateTimeauthor usestype=utcwhen UTC formatting is semantically required; otherwise device local time is the default. An explicitbandorpointinstead requests a discrete, sparse DateTime axis and therefore cannot simultaneously request theutcscale. Endpoints should choose bucket instants whose local labels are unambiguous for their audience.
Normative broken-time interval-lane policy
Owner: 2d.md#normative-broken-time-interval-lane-policy
-
chart.lane.when-to-use(judgment): This pattern is for sparse, interval-like quantities whose visual comparison is more useful than a calendar-proportional gap. It makes occupied buckets legible without pretending that empty calendar time is measured. It is not a time series, event timeline, duration chart, or a client-side stacking feature. -
chart.lane.provider-owns-geometry: The provider owns every geometric decision. Its row schema uses the generic guide attribute explicitly:Rust#[libertas_chart_channel(x)] #[libertas_chart_scale(id = usage_display, kind = linear)] display_start: f64, #[libertas_chart_channel(x2)] display_end: f64, #[libertas_date_only] #[libertas_chart_channel(tooltip)] #[libertas_chart_guide( target = x, source = span, position = bottom, title = none, grid = none )] bucket_starts_on: u32, #[libertas_chart_channel(tooltip)] amount: f32, -
chart.lane.display-coordinates:display_start,display_endmust be finite numeric coordinates and every valid positive contribution must havedisplay_end > display_start. They are explicitly display coordinates: neither field is an event time, and an automatic time guide must be absent. The provider supplies the real bucket date and exact quantity in tooltip fields. Those tooltip values, not the synthetic coordinate, are the factual date and amount presented to a person, copied to accessibility output, and used by details or drill-in actions. A bucket value is never implicit row identity: every same-bucket source contribution remains a distinct row unless the author provides an explicitkeychannel. -
chart.lane.anchor-grouping: For a response, group all valid rows by the exact real bucket anchor across all lanes. Sort those anchors ascending, start the first display anchor at zero, and give every lane in a group that same anchor. Use one response-wide quantity denominator. For each lane, allocate the ordered positive contributors from cumulative quantity boundaries, preserving contiguity and reserving a whole synthetic unit for every remaining contributor. The group extent is the largest resulting rounded lane extent. Thus rounding cannot make another lane overlap or cause its next group to relocate. -
chart.lane.fixed-gap-parameters: For the sprinkler V1 contract, the full-scale extent is exactly 600 synthetic seconds. Letprevious_extentbe the preceding real bucket's group extent and letdeltabe the difference between consecutive real bucket anchors. The next display anchor isprevious_anchor + previous_extent + deltawhen0 <= delta < 60, andprevious_anchor + previous_extent + 30whendelta >= 60. Equal anchors are one group, not a zero-gap second group. The 30-unit collapsed gap is five percent of 600 and leaves at least four integer display units for one-unit positive-contributor floors. A later contract version may choose different response-wide fixed parameters, but it must document them as a provider contract; clients must never infer or normalize a gap locally. -
chart.lane.provider-rounding: The provider uses stable contributor ordering (for sprinkler: rain, irrigation, forecast rain, scheduled water), a deterministic sort by real bucket anchor, and cumulative-boundary rounding rather than independently rounded segment widths. It omits nonfinite or nonpositive inputs. If no valid positive interval can be produced for a lane, it returns the ordinary empty-lane annotation rather than a zero-width mark or fabricated value. Saturating coordinate arithmetic must preserve order where representable; a provider that cannot emit a finite, strictly positive interval omits that invalid row rather than wrapping or relocating unrelated rows. -
chart.lane.client-literal-mapping: The client maps valid endpoints literally into the continuous x range and the categorical lane into its band. Apart from the explicit span guide above, it does not aggregate, sort, stack, renormalize, replace gaps, relabel the x axis as time, or repair provider geometry. It applies the paint-only minimum and matched clipping/hit-testing footprint defined in Section 5.3; that presentation rule never changes this provider geometry. The tooltip and accessibility label must state the real bucket date and exact amount, never synthetic seconds. -
chart.lane.provider-mapping: The provider owns this precomputed visual-coordinate contract. It must use one consistent chart-wide mapping and emit a finite, nondegeneratex,x2interval for every positive segment; a clipped edge, rounding step, or empty contributor must not corrupt unrelated geometry. The client maps the supplied endpoints literally. It does not aggregate, normalize, stack, facet, or repair these rows. Responsive renderers may guarantee a minimum visible pixel width for a valid positive interval; that presentation aid is not a substitute for valid provider geometry and does not change the data-space mapping. The explicit span guide replaces the misleading numeric axis for synthetic coordinates. This uses the genericChartGuideattribute and adds no special bucket, histogram, stack, or timeline grammar.
6.2.2 Domain contributors and baselines
Owner: 2d.md#622-domain-contributors-and-baselines
chart.domain.automatic-zero: Automatic continuous domains cover the finite visible data. One-sided quantitativebarand area baselines include zero by default. Line, point, explicit area bands, rule, rect, polygon, and ranged bar marks fit their explicit data by default.zerooverrides that choice when the scale type permits it. If exactly one explicit bound lies beyond every automatic domain contributor, or there are no contributors, the unresolved side collapses to that explicit bound. The result is the degenerate one-tick domain defined above; clients do not ignore the bound or invent platform-specific padding merely because all supplied marks are clipped.chart.domain.guide-visibility: A continuous scale-sourced guide is visible when its resolved shared scale has at least one eligible domain contributor anywhere under the selected chart root, or when the author supplied a domain anchor (min,max, paintmid, orzero=true). Thus an empty first binding can still own the guide for data contributed by a later binding with the same scale ID, and an explicitly bounded empty snapshot retains its truthful authored guide. A fully automatic scale with no contributors owns only an internal transform placeholder and shows no axis or legend. A categorical scale-sourced guide likewise requires at least one resolved category. A member of a closed Enumeration appearance domain is resolved even when the current snapshot has no row for that member; positional and facet guides remain eligible-row-driven.chart.domain.shared-zero: For a shared scale whose bindings all omitzero, the automatic domain is the union of every contributing binding's domain contribution, so any one-sided bar or baseline area contributes zero. An explicit sharedzero=falsedisables that automatic contribution for the complete shared scale;zero=trueforces it. This rule does not depend on composition traversal order.chart.domain.number-bounds-not-domain:NumberMinandNumberMaxremain input/data validation constraints. They do not silently become chart domain bounds. Authors useChartScale(min=...,max=...)when a stable visual domain is required.
6.3 Shared and independent scales
Owner: 2d.md#63-shared-and-independent-scales
chart.shared-scale.scope: A scaleidis scoped to the outermost rendered chart root. Compatible bindings with the same ID share their domain and scale configuration, never their physical pixel range. Each unit chart maps that shared domain into its own plot rectangle. Thus vertically concatenated price and volume charts can align in time without sharing screen coordinates.chart.shared-scale.unnamed-local: An unnamed scale is always local to one unit chart. There is no implicit cross-layer sharing based on coincidental field names. Different IDs create independent scales, which supports multi-scale layers without a separate Vegaresolvegrammar; §6.4 defines their deterministic guide layout.chart.shared-scale.endpoint-domain: domain inference includes the primary and secondary endpoint values from every binding carrying that ID;chart.shared-scale.property-fill: declarations may repeat identicalmin,max,mid,zero, andreverseproperties, while one of those omitted properties is filled from another binding or inferred automatically;chart.shared-scale.continuous-union: continuous domain contributions union across all bindings, including the automatic baseline rule above;chart.shared-scale.paired-inherit: Paired endpoint fields inherit their primary field's scale and do not repeat the ID. This is how a candlestick's wick and body share time and price domains. The body still has its own pixel geometry, and an optional volume subplot uses an independent y-scale while sharing only the time ID.
6.4 ChartGuide: generic guides
Owner: 2d.md#64-chartguide-generic-guides
-
chart.guide.attribute:ChartGuideis the fourth chart attribute. It separates guide presentation fromChartScaledomain and transform semantics. Itspositionreaches clients through the Display Template. Its generated value contains properties in this canonical order:Texttarget,source,position,title,domain,ticks,labels,grid
6.4.1 Placement and validation
Owner: 2d.md#641-placement-and-validation
chart.guide.auto-defaults:position=autoresolves to bottom for x-oriented guides, left for y-oriented guides, top for column headers, bottom for legends, and the intrinsic perimeter/ray layout for polar guides. Automatic row headers appear above each row in a single-column facet stack, preserving the plot's horizontal space; in a multi-column facet grid they appear on the left. Explicitleftorrightrow-header positions remain on the requested side.domain=automeans the Cartesian axis baseline or polar boundary/ray. An appearance legend's sample or ramp is intrinsic and is not an axis domain component. For an offset tier, the domain and ticks repeat within each parent band, and its grid strokes are clipped to that parent band. Headers have no domain line.ticks=automeans axis, data-anchored, offset-tier, or continuous-legend tick strokes; categorical legends and headers have no ticks.grid=automeans Cartesian grid lines, theta spokes, radius circles, or repeated offset separators. Defaults areposition=autoand every component supported by the inferred guide isauto; unsupported components default tonone. A value or span guide defaultstitle=noneand every other supported component toauto;source=nonepermits no optional property.chart.guide.kinds: The inferred scale guide is an axis for Cartesian or polar positions, a nested categorical label tier for an offset, a facet header forroworcolumn, and a legend for an appearance role. There are no bucket-date, radar, map, or other chart-family guide types.
6.4.2 Ownership, layers, facets, and composition
Owner: 2d.md#642-ownership-layers-facets-and-composition
chart.guide.label-candidates-local: An explicitsource=valueorsource=spanguide takes candidates only from the declaring unit's rows; sharing its target scale with another layer does not join row data across units. Its output positions nevertheless use the complete resolved shared domain.chart.guide.title-owner: A resolved guide takes its title policy and localized title source only from that owner. Forsource=scalethe source is the owner's target-binding field; forsource=valueorsource=spanit is the guide-label declaration field. Documents on other shared-scale contributors are ignored. Matching or differing rendered title text never changes scale sharing or chart validity.chart.guide.side-order: Within one pane, guides on each resolved physical side sort by(target,unit); the first is nearest the plot and later guides stack outward. Facet copies preserve that order. Explicit sides do not move responsively. Automatic x guides stack at bottom, automatic y guides at left, and automatic legends at bottom; an author usespositionto distribute them. No localized text, runtime value, measured label width, or native traversal order changes this semantic ordering. The old compound multi-axis fallback does not exist.
6.4.5 Scale ticks, labels, and typed formatting
Owner: 2d.md#645-scale-ticks-labels-and-typed-formatting
chart.guide-label.text-sources: Guide text never comes from aChartGuideproperty: its grammar contains no literal or format string. On the exact owner field defined in §6.4.2, a title resolvesChartGuideTitle, then the localized documentName;title=noneomits it. Boolean values use the client's localized true/false resources while rawfalse,trueremain identity and order.chart.guide-label.authored-format: A nonempty authoredFormaton the exact guide owner is authoritative.chart.guide-label.typed-resolution: Enumeration labels use their member documents. FormattedText Bytes use their declared resource catalog and arguments; an unresolved or malformed essential formatted value falls back the chart rather than exposing its resource key or argument diagnostics.Device,VirtualDevice,User,Endpoint, andLanDeviceuse the ordinary permission-aware system object-name resolver. Resolution never changes raw identity, grouping, ordering, or scale mapping. An unavailable object uses the resolver's existing localized unavailable-name result; if that contract supplies no safe display value for an essential label, the chart falls back instead of exposing a raw ID. Fixed program tokens must be Enumeration or FormattedText. A value that identifies a Libertas system object must retain its typed object reference and use the ordinary resolver; it must not copy the resolved or source-language name into a String. A plain String is visible only when it is genuine runtime/domain/user text and is displayed literally. Generic words, overflow indicators, and accessibility phrases come exclusively from client resources. Native names, scale IDs, channel tokens, and raw object IDs are never synthetic user-visible fallbacks.
6.4.6 Value and span guide sources
Owner: 2d.md#646-value-and-span-guide-sources
chart.guide-label.value-span-eligibility: A value/span row is guide-eligible independently of mark eligibility through a two-phase rule. Before domains resolve, both sources require a non-null typed label and every authoredrow/columnfacet binding needed to place the row;valueadditionally requires one finite, type-valid, transform-valid raw target primary, whilespanrequires both target endpoints. Those raw target values contribute only to the target scale domain, and the facet identities contribute only to their facet domains. Unrelated position and appearance fields do not contribute on behalf of the guide. After all mark and guide contributions have resolved shared scales and panes, the client maps and clips these provisional candidates; a mapped-invalid or fully clipped result emits no guide entry.chart.guide-label.guide-only-rows: Another required mark position may be null, so an App task can emit a guide-only row that produces no painted data mark. The row nevertheless keeps its ordinary path meaning: it breaks a line or area run or separates a polygon subpath when the missing role ordinarily does so. This is the explicit generic path for task-computed ticks or intervals after an arbitrary transformation; clients never have to infer the transformation from the label.chart.guide-label.value-candidates: A value candidate retains its source row's typed raw label identity and mapped target coordinate. A null label omits that candidate. Failure to localize or resolve a non-null label follows the essential-text fallback in §8.1; it never silently removes a semantic candidate. Within one guide instance and facet pane, rows with the same typed raw label identity and the same canonical raw target identity collapse to one candidate whose ordering index is the first row; this does not give the guide an interactive source-row owner. Equal labels at different target values remain distinct. Candidates are ordered by mapped output position, then canonical row order, and use the same collision thinning as a discrete scale. A value guide with no valid semantic candidate resolves no guide instance: it draws no title, domain, tick, label, or grid. Authors who need an empty bounded axis usesource=scale.chart.guide-label.span-grouping: A span guide groups eligible rows within one unit and facet pane by exact typed raw label identity, never by localized text. For each group it maps and clips every valid authored primary/secondary interval to the current plot viewport, normalizes endpoint order, discards empty intersections, and merges overlapping or touching intervals. It emits one guide entry for each connected component at that component's mapped midpoint. It does not bridge a visible gap, average source coordinates, derive a time distance from the label, or invent any domain coordinate beyond the raw endpoints contributed by the pre-domain rule above. Components order by mapped midpoint and then the first contributing row. Zoom recomputes clipping, components, and collision from the unchanged raw snapshot. The first and last visible candidates remain priority labels.chart.guide-label.tooltip-isolation: This is the complete generic rule for sparse or task-transformed interval labels. A tooltip field can affect a guide only when it explicitly carriesChartGuide; adding an unrelated tooltip can never change guide semantics.
6.4.7 Legend lowering and layout
Owner: 2d.md#647-legend-lowering-and-layout
chart.legend.guides-inert: Guides are semantically inert. They do not compete in mark hit testing, own a tooltip or source row, trigger a typed request, or become a selection. A value or span label remains guide-inert even though rows supplied it; authors use atextmark when the label must be interactive.chart.legend.guide-resource-limits: Guide projection is bounded before native allocation: at most256resolved guide instances under one chart root,4096semantic candidates for one guide,16384guide entries in total, and32768guide primitives in total. A title, label, tick, grid line/spoke/circle, domain stroke, or legend sample is one guide primitive. These counts are separate from the data-mark primitive budget; responsive thinning may reduce native objects but cannot make an over-limit semantic guide valid. Text retains the existing 4096-byte per-value limit.
Preview chart height
Owner: 2d.md#preview-chart-height
-
chart.preview.height: Preview height. A preview's charting-region heightRfollows its pane. LetWpbe the width assigned to the chart's plot-and-guide frame before any guide lane, which is the preview shell widthWvfor a unit, layer, facet, orvconcatand the pane widthPbelow for anhconcatchild; letHpbe the visible height of the nearest enclosing pane under the Display Template's height tokens. The family aspecta(width over height) is1for a polar chart,4/3for a Cartesian chart while the pane'swidthcondition iscompact, and16/9for every other width class. The cap sharecis0.5. A Display Templatechartrule on the chart root may replaceawithaspect(squareis1,4:3,16:9) andcwithmax_height(small0.25,medium0.40,large0.60):TextR = max(180, min(Wp / a, c * Hp)) R = 180 when max_height is "thumbnail"
7. Why four attribute families are sufficient
Owner: 2d.md#7-why-four-attribute-families-are-sufficient
chart.attr.time-projection: Time is not a privileged client-side transform. An App task may emit a native DateTime/DateOnly position for the ordinary automatic temporal scale, or it may project time into any bounded numeric coordinate its domain requires. In the latter case, a typed DateTime, DateOnly, Enumeration, FormattedText, or Libertas object-reference field can independently supplysource=valueorsource=spanguide labels, including on guide-only rows whose unrelated mark geometry is null. The client therefore localizes truthful typed labels without reverse-engineering calendar meaning from synthetic geometry. This ability to execute arbitrary application code before returning one typed, chart-ready snapshot is a defining Libertas boundary, not a missing client transform language.
8. Attribute resolution and references
Owner: 2d.md#8-attribute-resolution-and-references
-
chart.attr.nearest-complete: Libertas attribute lookup returns the nearest declaration for one attribute name. Conceptually, lookup considers: -
chart.channels.override-complete-set: It does not union values from several declarations. Therefore a local override must contain the complete channel set:Textreferenced type: ChartChannel=tooltip use site: ChartChannel=x,tooltip correct use site: ChartChannel=x tooltip is intentionally replaced -
chart.attr.names-compose: Different attribute names compose normally. A localChartChannelcan use a referenced type'sChartScaleorChartGuidewhen no nearer same-named attribute replaces it. A localChartGuideis always a complete declaration; its properties never merge with an inherited guide. -
chart.attr.foreign-preserved: Foreign compatible types preserve official chart attributes unless the local schema supplies a valid explicit override. Both language parsers must extract the same local declarations and references; Studio's shared validator applies the portable placement, inheritance, canonicalization, and override rules.
8.1 Localization and schema-document dependency
Owner: 2d.md#81-localization-and-schema-document-dependency
-
chart.l10n.document-never-semantic: The second step reuses the same already-resolved Libertas document used by the ordinary data view, using the existing localized package document model. Chart support adds no document fetch, no chart-specific document format, and no document reference insideChart,ChartChannel,ChartScale, orChartGuide. Document text can never change data meaning or make an otherwise invalid chart valid. -
chart.l10n.enum-documents-required:Existing localized source Chart use Required? Enumeration member NameandDescriptionFixed category label and help Required only when that Enumeration is visibly encoded. -
chart.l10n.string-resources-runtime-keys:Existing localized source Chart use Required? Package document StringResourcesRender a FormattedTextBytes tuple with Libertas Message Format 1 (LMF1)Used when supplied; schema validation checks the declared package keys but does not prove every runtime tuple key. -
chart.l10n.custom-property-not-token-map:Existing localized source Chart use Required? Flattened custom document property Static App-specific chart wording Never required by the minimum grammar and never a runtime-token map. -
chart.l10n.optional-custom-properties: The minimum chart introduces no mandatory custom document keys. ExistingDisplayName, long description, field names/descriptions, and Enumeration documents are sufficient. An App may optionally provide localized flattened properties such asChartGuideTitle,ChartTooltipName,ChartEmpty, orChartAccessibilitySummarywhen the established text is not appropriate. The corresponding ordinary name/description or a built-in localized client resource remains the fallback; these properties never affect chart validation. They are static wording attached to the documented schema node. A client must never use them as a dictionary for arbitrary plain String payload values. -
chart.l10n.formatted-runtime-text: Formatted runtime text has a separate, closed path. The App schema declares one package-ownedStringResourcescatalog, and its package document contains the localized LMF1 templates. The emitting task already identifies that package; the tuple therefore carries no type owner or resource FQN. Schema validation checks declared keys and default-document membership but does not prove every runtime tuple key. At chart presentation time, an unresolved or malformed optional formatted value is omitted and an essential one invokes the chart failure policy in §10 for the complete chart. Resource keys, tuple bytes, and typed argument diagnostics may appear in bounded developer diagnostics or the ordinary data view's established fallback, but never as chart presentation text. -
chart.text.formatted-bytes: Formatted chart values never use a String containing Base64. Encoding only to decode again at render time provides no benefit, while the Bytes host creates a sharp distinction from literal text and makes authoring mistakes less likely. -
chart.text.lmf-format-complete: The LMF1 number or date style is the complete formatting instruction for that message argument; field-levelFormatand chart scale formatting do not override it. A locale, translated template, number style, or resolved object or Enumeration label may change text measurement and therefore label collision, guide reservation, legend flow, or chart presentation failure. It cannot change the data-derived plot geometry, domain membership, category ordering, shared scale compatibility, or interaction identity. -
chart.text.no-runtime-template: The payload never contains a template. The raw key/blob remains the category, grouping, ordering, and interaction identity. Two values that render to the same text therefore remain distinct. -
chart.l10n.static-wording-source: static App-authored chart wording comes from the resolved document or an optional custom document property; -
chart.l10n.typed-labels: localized Enumeration text comes from Enumeration documents; -
chart.l10n.dynamic-formatted: App-authored dynamic presentation usesFormattedText; the App package'sStringResourcescatalog supplies localization; -
chart.l10n.string-never-resource-key: a plain String payload is always literal data, never an implicit resource key; a fixed localizable program token must instead be an Enumeration or formatted Bytes output, and a Libertas object name must instead come from its typed object reference and normal resolver. -
chart.l10n.uniform-scope: These rules apply uniformly to every primitive and server-projected family, including direct labels, heatmaps, bubbles, networks, and Sankey-style flows. Changing geometry does not create an exception for presentation provenance. Onlykey-only Strings and guide-less identity appearance Strings are non-textual. A String that also participates in a tooltip, label, guide, legend, facet, or accessibility distinction is user-visible and must satisfy the literal-data assertion above. -
chart.l10n.bubble-product:Chart family and field Required semantic source Bubble portfolio productPlain String only when it is the product's proper name; use an Enumeration for a fixed product class. -
chart.l10n.heatmap-time-block:Chart family and field Required semantic source Activity heatmap time_blockEnumeration for fixed blocks, or formatted Bytes for parameterized wording. -
chart.l10n.direct-label:Chart family and field Required semantic source Direct-label labelEnumeration, formatted Bytes, typed Libertas object reference, or genuine literal domain/user text according to provenance. -
chart.l10n.network-node-connection:Chart family and field Required semantic source Network node/connectionTyped Libertas object references when they identify system objects; otherwise Enumeration for a closed relationship vocabulary or formatted Bytes for dynamic wording. -
chart.l10n.sankey-stage:Chart family and field Required semantic source Sankey stageEnumeration for fixed stages, or formatted Bytes for parameterized wording. -
chart.l10n.identity-and-key-strings:Chart family and field Required semantic source Identity-scale colors and key-only identifiers Plain String is allowed only while the field has no text-emitting role or guide. -
chart.l10n.enum-label-not-flattened: An enum-backed traffic-source arc label already satisfies this rule because the client resolves its member document; it must not be flattened into a String. -
chart.l10n.essential-text: Essential chart text means atextmark value, a visible positional, facet, or appearance category, a retained guide or legend label, or an accessibility name needed to distinguish rendered items. Chart and guide titles, descriptions, tooltip captions, and nonessential tooltip values are optional and are omitted when their localized presentation is unavailable.
9. Interaction model
Owner: 2d.md#9-interaction-model
chart.interaction.categories-preserved: a Cartesian physical plot preserves every authored category on positionalbandandpointscales. It never samples, aggregates, drops, or replaces a category. Only the App task may intentionally return already-aggregated category rows, which are then ordinary authored chart data;chart.interaction.selected-row-source: a selected mark retains its sourceScriptValue, allowing existing typedNextRequestandCopyFromhandling to be reused where applicable;chart.interaction.carrier-context-retained: chart projection also retains the complete response or selected Union variant. Hidden fields in a unit carrier, composition, or Union wrapper are therefore available to the same typed transaction without becoming visible chart data. A renderer must not clone only the projected List and discard its carrier ancestors;chart.interaction.data-change-new-snapshot: a data-changing action sends a protocol request and replaces the chart with the returned snapshot.
10. Validation and fallback
Owner: 2d.md#10-validation-and-fallback
chart.text.review-literal-provenance: Package conformance review and response-fixture tests additionally validate the provenance assertion that cannot be derived from schema JSON: every visible plain String is genuine literal domain/user text. Fixed program vocabularies use Enumeration or FormattedText, and Libertas object labels use their typed system-object reference. This audit covers every role that can emit text and explicitly excludes only key-only and guide-less identity appearance Strings.chart.fallback.policy: Chart failure policy: when a chart cannot be rendered, the implementation may provide a complete ordinary-data fallback or display a bounded localized error message. This choice applies to 2D and 3D charts, including default or automatic previews and Chart Explorer. It covers invalid chart presentation, unsupported features or future tokens, layout failure, and recoverable resource failure.
11. Detailed examples
Owner: 2d.md#11-detailed-examples
chart.example.document-chart-fields(judgment): The examples use the implemented TypeScript or Rust source syntax. The first cross-language example shows ordinary short and long documentation. Later examples omit most comments for geometric clarity, but their native field names never stand in for user-visible labels. Production packages supply localized names and descriptions through the existing document pipeline; optional labels are omitted and essential category failures follow §8.1 and the chart failure policy in §10.
11.7 Candlestick chart
Owner: 2d.md#117-candlestick-chart
chart.example.layers-not-joined: Both layers repeat all desired OHLCV tooltip fields because the client does not join rows across layers, even when theirkeyvalues match. The key preserves conceptual identity and focus stability only. Both literal paint fields declare an identity scale and are not rendered as text. The fixeddirectionwording is an Enumeration whose localized member label is used by the tooltip. The single-pixel zero-height rule from the geometry contract keeps anopen == closedoji visible.
11.10 Server-projected maps, networks, and flows
Owner: 2d.md#1110-server-projected-maps-networks-and-flows
chart.example.server-projected-geometry(judgment): The minimum grammar deliberately has no geographic projection, topology, force layout, contour generation, or Sankey transform. The endpoint returns projected and sampled geometry that the primitives can express:chart.example.projected-region-polygon: a filled projected region or choropleth region as a polygon detail group, using null-position separators for islands and holes and repeating one canonical raw fill value on all of its vertices;chart.example.projected-text-source: Projected geometry does not relax the presentation-source rule. The field-by-field audit in §8.1 is normative for bubbleproduct, activitytime_block, directlabel, networknodeandconnection, Sankeystage, and traffic-source values. Apps must not copy an object's current display name or emit fixed English tokens in a visible plain String. Reusing one of these row schemas in another response, such as an operations dashboard reusing the activity heatmap, does not create an exception.chart.example.app-owns-projection: This provides useful projected-map and flow/network charts without embedding a geospatial, contour, or graph runtime in every client. The App remains responsible for projection, antimeridian decisions, topology/ring extraction, force simulation, contouring, and path sampling. A client does not interpret geographic coordinates or topology merely because a polygon happens to depict a map.
11.11 Scatter and bubble chart
Owner: 2d.md#1111-scatter-and-bubble-chart
chart.example.literal-point-radius: If the endpoint supplies a literal point radius in density-independent points, it usestype=identityand must stay within the client resource limit.
11.13 Filled radar chart
Owner: 2d.md#1113-filled-radar-chart
chart.example.radar-app-normalizes(judgment): A radar chart is not a special mark. The App assigns every indicator an explicit turn, normalizes that indicator's value against its own documented bound, and emits one polar polygon detail group per series. The original value and indicator remain tooltip data. The client does not infer indicator maxima or evenly space categories.
12. Security, resource, and accessibility requirements
Owner: 2d.md#12-security-resource-and-accessibility-requirements
chart.limits.structural: impose the same version-one structural and metadata limits on both platforms: composition depth8, unit charts64, facets64, resolved guides256, candidates per guide4096, total guide entries16384, total guide primitives32768, tooltip fields per row32, and UTF-8 bytes in one text, guide label, or tooltip value4096;chart.limits.no-row-ceiling: impose no chart-specific numeric ceiling on source rows, eligible source primitives, or vertices in a source path. The enclosing ScriptValue transport and ordinary-data model already own payload admission; chart presentation must not reject the same admitted snapshot because it crosses an unrelated conservative geometry count;chart.a11y.color-not-only-carrier(judgment): require any meaning encoded through paint to also be exposed through a localizedtooltip,detail, text, shape, or equivalent accessibility value, so color is not the only carrier of state;chart.limits.depth-and-unit-counting: the outer selected chart root has composition depth0; each composition edge adds one, so a unit reached through at most eight composition edges is permitted. Every mark-bearing List leaf is one unit chart; composition containers do not consume the64unit-chart budget;chart.limits.facet-counting: a facet grid contains the Cartesian product of its resolvedrowandcolumndomains, with a missing dimension contributing a factor of one. The64limit is the sum of panes produced by faceted plot nodes under the outer chart root. Sibling units in onelayercount one grid only when their ordered raw row and column domains are identical; otherwise each distinct grid contributes its own product.
14. Deliberate exclusions and future evolution
Owner: 2d.md#14-deliberate-exclusions-and-future-evolution
chart.exclusion.client-transforms: client-side transform and expression languages;chart.exclusion.option-blobs: arbitrary JSON option blobs;chart.exclusion.client-joins: client joins, lookups, aggregation, binning, or stack inference;chart.exclusion.field-paths: arbitrary field paths;chart.exclusion.url-html-marks: raw URL or HTML marks;chart.exclusion.client-geo: client-side geographic projection, topology/ring extraction, force layout, contouring, curve interpolation, or path sampling;chart.exclusion.signals: Vega signals and general event streams;chart.exclusion.brush-state: persistent crossfilter or brush state;chart.exclusion.patches: chart-specific incremental patch formats;chart.exclusion.special-marks: special marks whose geometry can be composed from primitives.chart.exclusion.emit-rows-first(judgment): New capability should first be tested against the core question: can the App server emit explicit rows and primitive layers instead? If yes, no schema extension is needed. A future attribute is justified only when it represents portable client interaction or presentation that cannot be supplied safely as typed chart-ready data.
Presentation decision
Owner: endpoint-presentation.md#presentation-decision
endpoint.presentation.quick-actions(judgment): an endpoint whose requests are all non-chart opens a protocol-only Quick Actions drawer;endpoint.presentation.workspace(judgment): an endpoint with one or more chart-producing requests opens a full-height Endpoint Workspace. On a phone this is a navigation destination. A wide client may use a full-height pane, but content gestures never dismiss it.endpoint.presentation.chart-boundary(judgment): Chart capability in the complete endpoint schema is the deterministic promotion boundary. A chart-producing request promotes the endpoint even when its request fields lack complete defaults and therefore cannot produce an automatic preview. For this contract, chart-producing includes a response or subscription-data path that resolves to either theChartor the strictly parallelChart3Dpresentation namespace. Loading state, default completeness, response success, chart row count, orientation, and prior visits must not change the selected presentation after it opens.
Endpoint Workspace
Owner: endpoint-presentation.md#endpoint-workspace
endpoint.workspace.actions-section(judgment): the top Actions section contains every non-chart request and every chart-producing request that lacks complete executable defaults;endpoint.workspace.charts-section(judgment): the following Charts section contains the automatic previews whose chart-producing requests have complete executable defaults.endpoint.workspace.configure-variables:Nilis never a variable.FixedandReadOnlyvalues are not variables, whileDefaultonly supplies an initial value and remains editable. A request whose effective tree contains no editable variable is not configurable and must not show Configure. Loading and unavailable previews may use the localized request label, but their Configure action follows the same rule.
Charts
Owner: presentation.md#charts
chart.appearance-in-template: Chart appearance belongs to the Display Template. Studio validates the complete chart metadata on the parser result, then compiles the scaletypeof eachChartScaleandChart3DScaleinto ascalehint and thepositionof eachChartGuideinto aguidehint, and removes those properties from the final schema; an attribute left with no property is removed. Channels, scale IDs, domains, and every other chart property remain schema. A template rule may also choose a chartpalette. Clients read the effective scale type, guide position, and palette only from the template, and a template value is validated under the same chart matrices as the source.chart.explicit-guides(judgment): Use@LibertasChartGuide(target=..., source=...)or#[libertas_chart_guide(target = ..., source = ...)]only for an explicit guide. Targets arex,y,xOffset,yOffset,theta,radius,color,fill,stroke,opacity,fillOpacity,strokeOpacity,size,shape,angle,strokeWidth,strokeDash,row, andcolumn. Sources arescale,value,span, ornone;valueis limited tox,y,theta, orradius, whilespanis limited to continuous Cartesianx/x2ory/y2. The required properties aretargetandsource. Optional properties areposition,title,domain,ticks,labels, andgrid; positions areauto,top,right,bottom, orleft, and component switches areautoornone. Prefer inferred scale guides unless the App needs an explicit override.
3D charts
Owner: presentation.md#3d-charts
chart.structured-view-meaningful(judgment): Always keep the ordinary structured data view meaningful. Charts are an additional presentation, not the only representation of returned data.
Rules Studio enforces
Studio rejects a source that breaks one of these rules. The owner holds the full rule.
2.1 Source-language annotations
Owner: 2d.md#21-source-language-annotations
chart.data.kind-not-type: Source annotations usekind; the generated scale property istype
3.1 Chart metadata belongs to returned data
Owner: 2d.md#31-chart-metadata-belongs-to-returned-data
chart.data.response-payload-only: Chart attributes go on a response or subscription-data payload typechart.data.unit-shape: A unit chart is a List of row Tables; the List carries the markchart.data.hidden-row-field: A Hidden row field carries no ChartChannel, ChartScale or ChartGuidechart.data.carrier-table: A carrier Table has exactly one non-hidden List field; every other field is Hiddenchart.compose.shape: A composition Table holds at least one chart child; every other child is Hiddenchart.data.variant-wrapper: A chart-wrapping Union variant has exactly one chart field; every other field is Hidden
3.3 Direct row fields only
Owner: 2d.md#33-direct-row-fields-only
chart.data.direct-row-fields: Chart channels go only on direct scalar fields of the chart row
4. Chart
Owner: 2d.md#4-chart
chart.mark.one-token: Chart has exactly one lowercase token as its generated Value
4.1 Primitive marks
Owner: 2d.md#41-primitive-marks
chart.mark.appearance-closed: Each mark accepts only the appearance channels in its row of the table
4.2 Compositions
Owner: 2d.md#42-compositions
chart.compose.layer-children: A layer child is a unit chart or a layer, never a concatchart.compose.layer-one-family: A layer is entirely Cartesian or entirely polar
5. ChartChannel
Owner: 2d.md#5-chartchannel
chart.channel.one-per-node: At most one Chart, ChartChannel, ChartScale and ChartGuide per schema node
5.1 Channel vocabulary
Owner: 2d.md#51-channel-vocabulary
chart.channel.positional-roles: ChartChannel roles are positional vocabulary tokens, never named properties
5.2 Multiplicity
Owner: 2d.md#52-multiplicity
chart.channel.singletons: Every role except tooltip and detail is a singleton; detail fields form one series keychart.channel.scale-bearing: A field carries at most one scale-bearing role; duplicate the value for a second scalechart.channel.scale-needs-role: No two fields share a singleton role; ChartScale needs a scale-bearing rolechart.channel.secondary-endpoints: A secondary endpoint needs its primary, shares its scale and type, and carries no ChartScale
5.3 Cartesian and polar geometry
Owner: 2d.md#53-cartesian-and-polar-geometry
chart.geometry.offsets: xOffset requires a discrete x band and yOffset a discrete y band; each creates a nested categorical bandchart.geometry.point: A point requires exactly one primary pair: Cartesian x,y or polar theta,radiuschart.geometry.line: A line requires exactly one primary pair: Cartesian x,y or polar theta,radiuschart.geometry.area-vertical: A vertical area uses x,y and optional y2chart.geometry.area-horizontal: A horizontal area uses y,x,x2; an area never uses both x2 and y2chart.geometry.bar-one-sided: A one-sided bar has one discrete axis and one quantitative Number axis from zerochart.geometry.bar-rect-axes: Each bar or rect axis is one discrete band field or an explicit intervalchart.geometry.bar-x-interval: bar(x,x2,y) needs continuous x and either a discrete y band or a Number histogram ychart.geometry.bar-y-interval: bar(x,y,y2) needs a discrete x band; with x2 and y2 both primaries are continuouschart.geometry.no-log-baseline: No log scale on a one-sided bar, a histogram y, or a baseline area ychart.geometry.rule-cartesian: A Cartesian rule needs x,y plus x2 and/or y2; no plot-spanning endpoint is impliedchart.geometry.rule-polar: A polar rule needs theta,radius plus theta2 and/or radius2chart.geometry.text: A text mark requires exactly one of x,y,text or theta,radius,textchart.geometry.polygon: A polygon uses x,y or theta,radius with no endpoints, offsets, text, size, shape or anglechart.geometry.one-family: One mark never mixes Cartesian and polar positions; area, bar and rect are Cartesian, arc polarchart.geometry.arc-theta: An arc requires theta and theta2; the server supplies explicit angular boundschart.geometry.arc-radius-pair: An arc uses both radius and radius2 or neither
5.5 Type inference
Owner: 2d.md#55-type-inference
chart.type.discrete-opt-in: Discrete Number scales need an integral non-duration Number; DateTime may use band or point
5.5.1 Physical units
Owner: 2d.md#551-physical-units
chart.unit.continuous-compatible: Unit-bearing chart fields use continuous scales; paired and shared bindings need the same unit
5.5.2 Scalar display formats
Owner: 2d.md#552-scalar-display-formats
chart.type.no-other-scalars: Other semantic types, tables, lists, unions, nil and plain Bytes are not chart scalarschart.type.compatibility-table: Each channel role accepts only the scalar types in the compatibility table
5.6.1 Display Template palette
Owner: 2d.md#561-display-template-palette
chart.palette.hint-validation: A palette rule targets a Chart or Chart3D root with one of the four tokens
6. ChartScale
Owner: 2d.md#6-chartscale
chart.scale.grammar-closed: The ChartScale grammar is closed: id, type, min, max, mid, zero and reverse onlychart.scale.scalar-syntax: ChartScale properties appear once; scalars are finite grammar numbers without leading zeroschart.scale.scalar-units: Scale scalars use runtime units: epoch seconds, seconds of day, duration seconds, yyyyMMdd
6.1 Properties
Owner: 2d.md#61-properties
chart.scale.id-identifier: A scale id is a schema identifierchart.scale.type-tokens: Scale type is one of linear, log, symlog, sqrt, time, utc, ordinal, band, point, identitychart.scale.invalid-is-error: Repeated or unknown ChartScale keys and invalid combinations are errorschart.scale.type-matrix: The scale-type matrix is closed; only integral non-duration Numbers become categorieschart.scale.matrix.xy-number: Numeric x and y use linear, log, symlog or sqrt; integral Numbers may use band or pointchart.scale.matrix.xy-temporal: DateTime and DateOnly x and y use time, utc, band or pointchart.scale.matrix.xy-time-only: TimeOnly x and y use linearchart.scale.matrix.xy-categorical: Categorical and FormattedText x and y use band or pointchart.scale.matrix.offset-facet: Offsets and facets take discrete values on a band scalechart.scale.matrix.theta-radius:thetaandradiustake linear, log, symlog, sqrt or identity scaleschart.scale.matrix.paint-categorical: Categorical paint uses ordinal; a plain String may use identitychart.scale.band-extent: Only scale-bearing primaries own scales; bar and rect discrete dimensions use bandchart.scale.time-interval: A TimeInterval scale is linear, log, symlog or sqrt, never discrete or identitychart.scale.min-max-order: Scale min must not exceed maxchart.scale.mid-in-domain: mid belongs to a continuous divergent scale and lies within explicit boundschart.scale.log-positive: A log scale and every value the App supplies to it are strictly positivechart.scale.sqrt-non-negative: A sqrt scale and every value the App supplies to it are non-negativechart.scale.zero-quantitative: zero only on quantitative continuous non-log scaleschart.scale.identity-literal: An identity scale declares no domain properties; its values are already portable literalschart.scale.zero-not-temporal: Temporal, categorical and identity scales cannot use zerochart.scale.band-point-positional: band and point only on discrete positional or facet channels; on DateTime they make sparse categorieschart.scale.temporal-types: time and utc need DateTime or DateOnly; log, symlog and sqrt need an ordinary Numberchart.scale.bounds-continuous: min and max need a continuous scale; mid needs a continuous quantitative paint scalechart.scale.secondary-no-scale: A secondary endpoint channel cannot declare a scalechart.scale.date-only-bounds: DateOnly min and max canonicalize to exact valid yyyyMMdd dates
6.2 Automatic domains
Owner: 2d.md#62-automatic-domains
chart.domain.time-only: TimeOnly row values lie in half-open 0..86400; bounds in inclusive 0..86400
6.3 Shared and independent scales
Owner: 2d.md#63-shared-and-independent-scales
chart.shared-scale.compatible-bindings: Bindings sharing a scale ID agree on channel family, scalar type, presentation class, Enumeration, Money, PhysicalUnit and Formatchart.shared-scale.range-class: Point-radius size and text-font size cannot share a scale IDchart.shared-scale.conflict-invalid: Conflicting properties on a shared scale ID invalidate the chart
6.4 ChartGuide: generic guides
Owner: 2d.md#64-chartguide-generic-guides
chart.guide.grammar-closed: The ChartGuide grammar is closed: target, source, position, title, domain, ticks, labels, gridchart.guide.required-keys: ChartGuide keys appear once; source tokens equal the generated keys
6.4.1 Placement and validation
Owner: 2d.md#641-placement-and-validation
chart.guide.scale-source-placement: source=scale or none goes on the field whose primary scale-bearing role is the targetchart.guide.label-field-placement: value and span guides sit on a scale-free label field; span needs continuous x or y with x2 or y2chart.guide.matrix.cartesian-axis: Cartesian axes: x top or bottom, y left or rightchart.guide.matrix.polar-axis: Polar axes take only position autochart.guide.matrix.offset-tier: Offset tiers: xOffset top or bottom, yOffset left or rightchart.guide.matrix.facet-header: Facet headers take only title and labels; row left or right, column top or bottomchart.guide.matrix.value-rail: Value label rails use their Cartesian axis side matrixchart.guide.matrix.polar-value: Polar value labels take only position autochart.guide.matrix.span-rail: Span label rails use their Cartesian axis side matrixchart.guide.matrix.suppressed: A suppressed guide declares no position or componentschart.guide.outside-row-error: A guide property outside its target row is an errorchart.guide.implicit-explicit: One explicit ChartGuide per target per unit replaces the implicit guide; identity and offsets default to none
6.4.2 Ownership, layers, facets, and composition
Owner: 2d.md#642-ownership-layers-facets-and-composition
chart.guide.shared-ownership: A shared scale ID in one layered plot owns one guide with at most one explicit declarationchart.guide.polar-one-per-family: A polar layer shows at most one theta and one radius guide
Money
Owner: presentation.md#money
money.shared-scales-match: Shared chart scales and paired interval endpoints require matching Money metadata