Skip to content

feat(lint): validate dashboard filter field-existence at build time (#3365) - #3382

Merged
os-zhuang merged 1 commit into
mainfrom
claude/dashboard-filter-validation-sfw8ka
Jul 21, 2026
Merged

feat(lint): validate dashboard filter field-existence at build time (#3365)#3382
os-zhuang merged 1 commit into
mainfrom
claude/dashboard-filter-validation-sfw8ka

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes#3365.

Problem

Since #2501 wired dashboard-level filters — the built-in dateRange picker (reserved name dateRange) and every globalFilters[] 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:

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

os validate / os build passed; the failure surfaced only when a user opened the dashboard. validateWidgetBindings (ADR-0021) already checked the widget → dataset binding 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 validateWidgetBindings with a new dashboard-filter-field-unknown error rule. For each dashboard filter (dateRange + every globalFilters[]), for each widget, it errors 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 }.

Effective-field resolution mirrors the objectui runtime (packages/core/src/utils/dashboard-filters.ts) exactly: explicit filterBindings string re-targets / false opts out (both win), then the legacy targetWidgets allow-list, then the filter's own default field (dateRange defaults to created_at).

The error names the dashboard, widget, filter, field, and object, e.g.:

executive_dashboard › widget total_accounts: inherits dashboard filter dateRange(close_date), but object crm_account (dataset "account_metrics") has no field close_date.
Set filterBindings: { dateRange: false } on this widget to opt out, or re-target to an existing field …

Because the wiring in validate.ts / compile.ts already fails the build on error-severity widget findings, this gates both os validate and os build with no CLI change.

No false positives

  • Registry-injected system fields (created_at — the dateRange default — plus updated_at, owner_id, organization_id, id, …) are treated as always-present, since they're injected by the objectql registry and never appear in object.fields.
  • Objects outside the validated stack (e.g. from another installed package) are unjudgeable, so they're skipped rather than flagged.
  • Relationship-path fields (account.region) are engine-resolved, not base columns, so they're skipped.

Acceptance criteria

  • os build / os validate fails 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.
  • 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.

Testing

  • packages/lint suite green (250 passed, 2 skipped), including 14 new cases: the repro, opt-out, valid/invalid re-target, created_at system-field default, globalFilters[] fields, custom filter names, targetWidgets gating, dotted relationship paths, unknown objects, and non-suppressibility.
  • Ran the new validator against the real showcase stack (both dashboards that declare dashboard-level filters — Revenue Pulse + Delivery Operations): 0 findings, confirming the cross-object filterBindings remaps and created_at default don't false-positive.
  • @objectstack/linttsup build (with DTS typecheck) clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BDsV6nCuuRPWbJhqkNBQzF


Generated by Claude Code

…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
@vercel

vercelBot commented Jul 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 21, 2026 1:47pm

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint.

1 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/permissions/authorization.mdx(via @objectstack/lint)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 21, 2026 13:53
@os-zhuang
os-zhuang merged commit fa006fb into mainJul 21, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/dashboard-filter-validation-sfw8ka branch July 21, 2026 13:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants

@os-zhuang@claude