Uh oh!
There was an error while loading. Please reload this page.
fix(showcase): scope the Delivery Operations status filter to the task widgets - #7612
Merged
Merged
Conversation
…k widgets
The dashboard-scoped global filter carried the showcase_task status
vocabulary but was declared as a bare `field: 'status'`, and the five
project-bound widgets declared no `filterBindings`. Since a widget
without bindings inherits a dashboard filter on its own object's
like-named field, the filter also landed on showcase_project.status —
whose value domain is disjoint — so those widgets emitted
`WHERE status = 'in_review'` against showcase_project, answered 200 OK
with a zero, and read 0 for any selection.
Name the filter `task_status` for the vocabulary it carries and opt each
project-bound widget out with `filterBindings: { task_status: false }`,
the same per-widget mechanism Revenue Pulse uses to map region ->
sales_region across two objects. dateRange stays inherited: projects do
carry created_at.
Adds a showcase test pinning the consequence rather than the key: every
value a global filter offers must be a value its effective field can
hold on each widget it reaches, and a filter must still reach at least
one widget.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0127HHmCr5vd3NudQmW6QiNNThe latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
os-zhuang
marked this pull request as ready for review
August 11, 2026 08:42
Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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.
Fixes#7568
Showcase authoring fix. The global-filter engine is untouched — it did exactly what the metadata told it to.
What was wrong
showcase_ops_dashboarddeclared its dashboard-scoped filter as a barefield: 'status'carrying the task vocabulary (backlog / todo / in_progress / in_review / done), and the project-bound widgets declared nofilterBindings. A widget with no bindings inherits a dashboard filter on its own object's like-named field, andshowcase_projectalso has astatus— with a disjoint vocabulary (planned / active / on_hold / completed / cancelled).So the inherited binding was field-valid and value-empty: the query was well-formed, the backend answered
200 OK, and the widget rendered a zero.packages/lint'sdashboard-filter-field-unknownrule cannot see this — it checks that the effective field exists, which it does.Which fix, and why
Both admissible options were considered:
filterBindingson the project widgetsscope: 'widget'+targetWidgetsA, for three reasons.
regionon the invoice widgets tosales_regionon the account widgets throughfilterBindings, verified on the wire and in the echoed SQL in the same QA run.targetWidgetsis described inpackages/lint/src/validate-widget-bindings.tsas the legacy allow-list thatfilterBindingsoverrides, and the browser bundle that would implementscope: 'widget'is not vendored here (packages/console/distis absent) — so option B could not be driven, only asserted. Demoing an unmeasured capability in the showcase is precisely what Prime Directive chore: version packages #10 forbids.The filter is also given the explicit name
task_status. A barestatusmade the opt-out read as "ignore project status" rather than "this control is about tasks"; with the name, the metadata says what the filter governs and every widget it does not govern says so on its own line.dateRangestays inherited everywhere on purpose — projects do carrycreated_at, so that filter is meaningful on both sides.A fifth widget
The issue names four.
table_spendis a fifth project-bound widget with the identical defect; it is fixed here too. It went unnoticed because an emptied table reads as "no data", not as a broken filter — a zeroed KPI tile at least looks wrong.Verification — driven, not read
The dashboard renderer reads published metadata, resolves each dashboard filter to a field per widget, ANDs it with the widget's own
filter, and issues onePOST /api/v1/analytics/dataset/queryper widget. A harness did exactly that against a live showcase backend (pnpm dev -- --fresh, seeded data, sqlite), reading the metadata fromGET /api/v1/meta/dashboard/showcase_ops_dashboard.Scope of the drive, stated plainly: the per-widget resolution is transcribed from
effectiveFilterField/dashboardFilterDefsinpackages/lint/src/validate-widget-bindings.ts— this repo's own executable mirror of objectui'sresolveBoundField. Everything downstream of that (metadata → wire → SQL → number) is the real server. The browser bundle itself is not in this repo and was not exercised; the objectui half remains a checklist item (dashboards.global-filters-rescope).Widget totals,
dateRangeheld unset in every capture so the status selection is the only variable:status=in_reviewtask_status=in_reviewkpi_active_projectskpi_at_riskkpi_total_budgetcol_healthtable_spendkpi_awaiting_reviewbar_statusdonut_priorityline_createdThe SQL the server echoed tells the same story. Before, under a selection:
After, under the same selection — the project side carries no
statuspredicate at all, and the task side still composes both filters:The QA checklist's Delivery Operations clause — "the global status filter composes with per-widget filters" — still holds, on the side where it is coherent.
The test pins the consequence, not the key
examples/app-showcase/test/dashboard-filter-vocabulary.test.ts, generic over every showcase dashboard:field:'status'with *task* statuses, and the project widgets carry nofilterBindings— so it also lands onshowcase_project.statusand zeroes those tiles #7568 case named in intent terms:task_statusgoverns the task widgets and no project widget.Reverse verification, direction predicted before running: revert
ops-dashboard.dashboard.tstomainand (1) and (3) go red, (2) stays green — the broken filter did reach widgets, it just could not answer them. Measured exactly that. (1) named all five affected widgets individually, includingtable_spend:Restored, all three green.
Gates run locally
pnpm --filter @objectstack/example-showcase verify(=os validate+tsc --noEmit+vitest run) — green; 18 files / 171 tests passed. Thevalidatestep is what runsvalidate-widget-bindings, the rule family this card sits next to.eslint --no-inline-configon both changed files — clean.node scripts/check-nul-bytes.mjs— OK, 7046 files scanned.No changeset:
@objectstack/example-showcaseisprivate: trueand absent from thefixedgroup in.changeset/config.json, so this PR releases nothing. Labelledskip-changeset.No
packages/specchange was needed, and no platform behaviour was altered.Generated by Claude Code