Found by opening a dashboard in a real ObjectStack application (objectstack-ai/duly) against published @objectstack/* 17.2.0. Source-confirmed afterwards in this repo.
The claim
A min / max measure over a date or datetime field cannot be rendered as a date on any dataset-bound widget, and the format escape hatch the spec offers for exactly this is unreachable for those values.
The KPI tile renders 2026-07-04T07:00:00.000Z where a date belongs — a 24-character ISO string in text-2xl font-semibold, which wraps to two lines inside the card.
Where it comes from
packages/plugin-dashboard/src/DatasetWidget.tsx:768 renders the dataset-bound metric value:
<spanclassName={cn('text-2xl font-semibold tabular-nums',accentClass)}>{formatMeasure(value,f?.format,f?.currency,f?.percentScale,displayLocale)}</span>and formatMeasure in packages/core/src/utils/dataset-format.ts opens with:
if(v==null)return'—';if(typeofv!=='number')returnString(v);
That second line is before format is touched. So for any non-numeric measure value the function is String(v) and nothing else — the format argument the call site carefully threads through is dead on this path. tabular-nums on the span is the same assumption stated in CSS.
Everything downstream of that guard is a numeral-pattern formatter (decimals from format.split('.')[1], %, $, currency), which no date pattern can enter.
Why the escape hatch does not close it
DatasetMeasureSchema in @objectstack/spec carries format: z.ZodOptional<z.ZodString> — an open string, so authoring format: 'YYYY-MM-DD' (or anything else) passes validate and build cleanly. It then has no effect, because the value never reaches the branch that reads it. An author gets a green toolchain and an unchanged raw timestamp, with nothing anywhere saying why.
Reproduce
Declare a measure aggregating a date field and bind it to a type: 'metric' widget:
// dataset{name: 'oldest_touch',label: 'Oldest touch',aggregate: 'min',field: 'last_update_at'}// dashboard widget{id: 'oldest',title: 'Oldest untouched task',type: 'metric',dataset: '<ds>',values: ['oldest_touch']}Open the dashboard. The tile shows the raw ISO string. Adding format: 'YYYY-MM-DD' to the measure changes nothing.
Scope beyond the metric tile
The same formatMeasure call is on three other surfaces, all with the same guard in front of them, so a date measure is raw in each: DatasetWidget.tsx:876 (chart value formatting), DatasetWidget.tsx:1083 (dataset table cells), and views/metadata-admin/previews/DatasetPreview.tsx:235. PivotTable.tsx:46 takes value: number outright.
Suggested direction
The narrow fix is to let formatMeasure recognise a date-shaped string (or a measure whose format names a date pattern) before the numeric short-circuit, and route it through the same date display path list cells already use — the argument for reusing that path rather than adding a second one is the same one the file's own header makes about percent, where two conventions drifted between a list cell and a dashboard measure (objectui#4576).
Whatever the resolution, the silence is the worse half: a format that the schema accepts and the renderer can never read is declared ≠ enforced. If dates are out of scope for measure formatting, the spec's format should say so and a date-typed aggregate should be refused at author time rather than rendered raw.
Application impact
Low severity — the number is correct and legible, just ugly. Reported because the silence is the reusable finding, not the wrapped timestamp.
Found by opening a dashboard in a real ObjectStack application (
objectstack-ai/duly) against published@objectstack/*17.2.0. Source-confirmed afterwards in this repo.The claim
A
min/maxmeasure over a date or datetime field cannot be rendered as a date on any dataset-bound widget, and theformatescape hatch the spec offers for exactly this is unreachable for those values.The KPI tile renders
2026-07-04T07:00:00.000Zwhere a date belongs — a 24-character ISO string intext-2xl font-semibold, which wraps to two lines inside the card.Where it comes from
packages/plugin-dashboard/src/DatasetWidget.tsx:768renders the dataset-bound metric value:and
formatMeasureinpackages/core/src/utils/dataset-format.tsopens with:That second line is before
formatis touched. So for any non-numeric measure value the function isString(v)and nothing else — theformatargument the call site carefully threads through is dead on this path.tabular-numson the span is the same assumption stated in CSS.Everything downstream of that guard is a numeral-pattern formatter (decimals from
format.split('.')[1],%,$,currency), which no date pattern can enter.Why the escape hatch does not close it
DatasetMeasureSchemain@objectstack/speccarriesformat: z.ZodOptional<z.ZodString>— an open string, so authoringformat: 'YYYY-MM-DD'(or anything else) passesvalidateandbuildcleanly. It then has no effect, because the value never reaches the branch that reads it. An author gets a green toolchain and an unchanged raw timestamp, with nothing anywhere saying why.Reproduce
Declare a measure aggregating a date field and bind it to a
type: 'metric'widget:Open the dashboard. The tile shows the raw ISO string. Adding
format: 'YYYY-MM-DD'to the measure changes nothing.Scope beyond the metric tile
The same
formatMeasurecall is on three other surfaces, all with the same guard in front of them, so a date measure is raw in each:DatasetWidget.tsx:876(chart value formatting),DatasetWidget.tsx:1083(dataset table cells), andviews/metadata-admin/previews/DatasetPreview.tsx:235.PivotTable.tsx:46takesvalue: numberoutright.Suggested direction
The narrow fix is to let
formatMeasurerecognise a date-shaped string (or a measure whoseformatnames a date pattern) before the numeric short-circuit, and route it through the same date display path list cells already use — the argument for reusing that path rather than adding a second one is the same one the file's own header makes about percent, where two conventions drifted between a list cell and a dashboard measure (objectui#4576).Whatever the resolution, the silence is the worse half: a
formatthat the schema accepts and the renderer can never read isdeclared ≠ enforced. If dates are out of scope for measure formatting, the spec'sformatshould say so and a date-typed aggregate should be refused at author time rather than rendered raw.Application impact
Low severity — the number is correct and legible, just ugly. Reported because the silence is the reusable finding, not the wrapped timestamp.