Skip to content

Validate dashboard filter field-existence at build time (extend ADR-0021) #3365

Description

@os-zhuang

Summary

Since #2501 wired dashboard-level filters — including the built-in dateRange picker (reserved filter name dateRange) — into every widget's analytics query, a dashboard whose filter field does not exist on a given widget's underlying object emits invalid SQL and the widget crashes at render time. This is a build-time-decidable invariant that currently escapes the entire static gate (os validate + os build) and only fails when a user opens the dashboard.

Surfaced upgrading a production app (HotCRM) from 14.7 to 16.0.0-rc.1.

Repro

A dashboard binds dateRange: { field: 'close_date' }. close_date exists only on the opportunity object; widgets on other objects (account / contact / lead / product) inherit the filter and produce:

SELECT COUNT(*) AS "contact_count" FROM "crm_contact"
WHERE close_date >= '2026-07-01' AND close_date <= '2026-09-30'
-- SqliteError: no such column: close_date (service-analytics executeRawSql)

os build / os validate pass; the error only appears at query time (logged-in user opens the dashboard), rendering the widget as an error card.

Root cause

os validate already runs "Checking dashboard widget bindings (ADR-0021)", but it validates the widget → dataset binding only — it does not check that a dashboard-level filter's field exists on each widget's dataset object. After #2501 those filters are applied to the widget query, so their field refs became a new class of runtime-only failure.

Proposed check (extend ADR-0021)

For each dashboard filter (dateRange + every globalFilters[]), for each widget, error unless:

  • the filter's effective field (after filterBindings re-target) exists on the widget's dataset object, or
  • the widget opts out via filterBindings: { <filterName>: false }.

This is the same field-existence invariant ADR-0032 already enforces for formula / sharing-rule CEL references — dashboard filter fields are the same category, just not yet covered.

Acceptance criteria

  • os build / os validate fails a dashboard where a filter field is absent on a bound widget's object and the widget has no opt-out, with a message naming the dashboard, widget, filter, field, and object, e.g.:

    executive_dashboard: widget total_accounts inherits dateRange(close_date), but object crm_account has no field close_date — set filterBindings: { dateRange: false } or re-target to an existing field.

  • A widget with filterBindings: { <name>: false } (or a re-target to an existing field) passes.
  • Fixture coverage for both the failing and opted-out shapes.

Happy to send a PR — the check is well-scoped and mirrors the existing ADR-0032 field-ref validator.


Related shift-left checks from the same 14.7 → 16 upgrade: #3365, #3366, #3367.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions