feat(lint): validate dashboard filter field-existence at build time (#3365)#3382
Merged
Merged
Conversation
…3365) Since #2501 wires dashboard-level filters (`dateRange` + every `globalFilters[]`) into every widget's analytics query, a filter field absent on a widget's dataset object emits invalid SQL (`no such column: …`) and crashes the widget at render time — a build-decidable invariant that escaped `os validate` / `os build` and failed only when a user opened the dashboard. Extend `validateWidgetBindings` (ADR-0021) with a new `dashboard-filter-field-unknown` error: for each dashboard filter × widget, fail unless the filter's effective field (after any `filterBindings` re-target) exists on the widget's dataset object, or the widget opts out via `filterBindings: { <name>: false }`. The message names the dashboard, widget, filter, field, and object, mirroring the field-existence invariant ADR-0032 enforces for CEL references. Effective-field resolution mirrors the objectui runtime (opt-out / string re-target / legacy `targetWidgets` allow-list / filter default). Registry-injected system fields (e.g. `created_at`, the `dateRange` default) and objects outside the validated stack never false-positive. Adds fixture coverage for the failing and opted-out shapes; verified 0 findings against the real showcase dashboards. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDsV6nCuuRPWbJhqkNBQzF
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
July 21, 2026 13:53
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3365.
Problem
Since #2501 wired dashboard-level filters — the built-in
dateRangepicker (reserved namedateRange) and everyglobalFilters[]entry — into every widget's analytics query, a dashboard whose filter field doesn't exist on a given widget's underlying object emits invalid SQL and the widget crashes at render time:os validate/os buildpassed; the failure surfaced only when a user opened the dashboard.validateWidgetBindings(ADR-0021) already checked thewidget → datasetbinding but not that a dashboard filter's field exists on each widget's dataset object — after #2501 those filter fields became a new class of runtime-only failure. This is the same field-existence invariant ADR-0032 enforces for formula / sharing-rule CEL references.Change
Extend
validateWidgetBindingswith a newdashboard-filter-field-unknownerror rule. For each dashboard filter (dateRange+ everyglobalFilters[]), for each widget, it errors unless:filterBindingsre-target) exists on the widget's dataset object, orfilterBindings: { <name>: false }.Effective-field resolution mirrors the objectui runtime (
packages/core/src/utils/dashboard-filters.ts) exactly: explicitfilterBindingsstring re-targets /falseopts out (both win), then the legacytargetWidgetsallow-list, then the filter's own default field (dateRangedefaults tocreated_at).The error names the dashboard, widget, filter, field, and object, e.g.:
Because the wiring in
validate.ts/compile.tsalready fails the build onerror-severity widget findings, this gates bothos validateandos buildwith no CLI change.No false positives
created_at— thedateRangedefault — plusupdated_at,owner_id,organization_id,id, …) are treated as always-present, since they're injected by the objectql registry and never appear inobject.fields.account.region) are engine-resolved, not base columns, so they're skipped.Acceptance criteria
os build/os validatefails a dashboard where a filter field is absent on a bound widget's object with no opt-out, naming the dashboard, widget, filter, field, and object.filterBindings: { <name>: false }or a re-target to an existing field passes.Testing
packages/lintsuite green (250 passed, 2 skipped), including 14 new cases: the repro, opt-out, valid/invalid re-target,created_atsystem-field default,globalFilters[]fields, custom filter names,targetWidgetsgating, dotted relationship paths, unknown objects, and non-suppressibility.filterBindingsremaps andcreated_atdefault don't false-positive.@objectstack/linttsupbuild (with DTS typecheck) clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01BDsV6nCuuRPWbJhqkNBQzF
Generated by Claude Code