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/lint tsup 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

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 21, 2026 1:47pm

Request Review

@github-actions github-actions Bot 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 main Jul 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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

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