Skip to content

fix(core): a dashboard dateRange that omits defaultRange takes the spec default preset (objectui#10339) - #10346

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10339-daterange-default
Sep 24, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10339-daterange-default

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Closes #10339
Clause-②: yes. The published runtime behaviour changes: an omitted defaultRange now filters to this_month. An isolated at-tier review happens before enqueue.

What

@objectstack/spec declares DashboardSchema.dateRange.defaultRange as z.enum(DATE_RANGE_DEFAULT_RANGES).default('this_month') (installed 17.4.0 and objectstack main at fc6ddb87 both). resolveDashboardFilterDefs in @object-ui/core treated an omitted defaultRange as "no filter", so dateRange: { field: 'created_at' } rendered UNFILTERED while the platform's parse of the same document says this_month.

Direction (i), per #7759 ruling 5617465269 rule 1: the read site applies the spec's default.

  • packages/core/src/utils/dashboard-filters.ts: when an authored dateRange has defaultRange === undefined, the preset is read FROM THE SPEC by parsing an empty element through DashboardSchema.shape.dateRange (imported from @objectstack/spec/ui, the same subpath this module already imports DATE_RANGE_PRESETS from; core already imports runtime zod schemas from the spec, e.g. FieldSchema in reference-keys.ts). No hand-copied string. Computed lazily on first use and cached, because DashboardSchema is a lazySchema and forcing it at module load would cost every importer of core.
  • An explicit defaultRange, 'custom' included, is unchanged. allowCustomRange is untouched (already consumed as !== false). No authored dateRange still yields no built-in filter.
  • content/docs/guide/dashboard-filters.md: the defaultRange bullet now states the omitted behaviour.
  • .changeset/10339-daterange-spec-default.md: minor on @object-ui/core, with the behaviour note. plugin-dashboard source did not change (tests only), so it carries no bump.

Blast radius (dashboards that will now show this_month)

Every authored dateRange object in examples/, content/, apps/ and packages/ source was enumerated with git grep -nE 'dateRange"?\s*:\s*\{' plus the multi-line and YAML forms. All authored dashboards (examples/schema-catalog/.../filtered-dashboard*.json x3, content/docs/guide/dashboard-filters.md, packages/plugin-dashboard/README.md) set defaultRange explicitly. Zero authored dashboards omit it, so no example, story or E2E fixture changes what it shows. packages/types/src/__tests__/dashboard-config.test.ts authors a dateRange without defaultRange, but that is the separate DashboardConfig (enabled/presets) shape, which resolveDashboardFilterDefs does not read. The only in-product producer is the metadata-admin designer: a dashboard saved there with a bare dateRange now opens on this_month, as the spec says.

Tests

  • core dashboard-filters.test.ts: three new cases. The omitted case resolves to { preset: 'this_month' } and compiles to month bounds. A second case pins that the value equals the spec schema's own parse of an empty element. A third keeps explicit defaultRange / allowCustomRange: false as authored and gives no def without dateRange.
  • plugin-dashboard DashboardFilterBar.dateDefault.test.tsx: the control for a bare dateRange reads "This month", not "All time".
  • plugin-dashboard DashboardRenderer.filters.test.tsx: a bare dateRange broadcasts a created_at range into the widget query.
  • Reverse verification (from committed af43adbc1, via objectstack scripts/ablation-replace.mjs): replaced ? specDefaultDateRangePreset() with ? undefined (anchor 1 to 0, blob 2761b23360ea to 124c918877e7). Result: Tests 4 failed | 53 passed (57), and the failures are exactly the four new cases. Restore then gave blob == HEAD and an empty git diff HEAD.
  • Final head 9bf3ea525 (after merging origin/main): built the @object-ui/plugin-dashboard^... closure; type-check on core and plugin-dashboard green; vitest run packages/core/src/utils/__tests__/dashboard-filters.test.ts packages/plugin-dashboard/src/__tests__/ gives Test Files 121 passed (121), Tests 1199 passed (1199).
  • Forward compatibility: spec built from objectstack main fc6ddb87, packed, and injected with node scripts/spec-main-shape-gate.mjs inject. Against it, core tsc --noEmit (src) exit 0, plugin-dashboard type-check exit 0, and the three touched test files 57 passed (runtime parse through main's strictObject dateRange). Then restored the pinned install; the restored spec dist/ui/index.mjs hash equals the shared checkout's pinned 17.4.0 and differs from the main build.
  • eslint --no-inline-config on the 4 changed TS files: 0 errors (17 pre-existing no-explicit-any warnings, none on changed lines). check-changeset-presence exit 0; check:new-line-citations gives 0 new citation(s); check-changeset-no-major exit 0.

Acceptance notes

  • Against objectstack main, core's tsconfig.test.json leg fails in an untouched file: normalize-list-view.pageResidual-8429.test.ts TS2344, "page" does not satisfy never. This is spec-main drift unrelated to this diff. The Spec Main Shape Gate CI job owns that reading.
  • The first commit carries a model-named Co-Authored-By trailer that I wrote by mistake. It is not rewritten, because force-push is banned here. Squash-merge text is the seat's to trim.

Generated by Claude Code

…kes the spec default preset

The spec declares DashboardSchema.dateRange.defaultRange with a default; the read site now applies it, derived from the spec schema rather than a copied string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nge.defaultRange; add changeset

Refs objectui#10339.
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3040.2 KB 3104.5 KB
Main entry chunk (gzip) 147.8 KB 350 KB
Entry file index-D-Q1mnWU.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 541.41KB 129.42KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 222.47KB 61.83KB
fields (index.js) 253.32KB 63.99KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 33.36KB 10.88KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.15KB 11.05KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 51.44KB 14.62KB
plugin-charts (index.js) 71.82KB 20.13KB
plugin-chatbot (index.js) 198.27KB 47.18KB
plugin-dashboard (index.js) 133.06KB 35.21KB
plugin-designer (index.js) 216.12KB 44.37KB
plugin-detail (index.js) 260.65KB 67.82KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 146.34KB 37.48KB
plugin-gantt (index.js) 167.99KB 41.37KB
plugin-grid (index.js) 215.46KB 58.88KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 113.90KB 28.11KB
plugin-map (index.js) 21.74KB 7.07KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.68KB 8.95KB
plugin-tree (index.js) 10.56KB 3.71KB
plugin-view (index.js) 85.18KB 21.05KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 110.46KB 36.33KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 9bf3ea5253eb416603c4fdad971fdd3f869f0403

① Derived judgments

Reviewed at 9bf3ea5, which spans 3 commits and 6 files (+126/−3).

The change is as claimed.

  • resolveDashboardFilterDefs now reads defaultRange === undefined ? specDefaultDateRangePreset() : defaultRange.
  • The rest of the dateRange arm is byte-identical to main: the created_at field default, the 'custom' handling that yields no default, and the allowCustomRange passthrough.

The default comes from the spec, not from a constant.

  • SpecDashboardSchema.shape.dateRange.parse({})?.defaultRange, imported from @objectstack/spec/ui, is computed on first call and cached.
  • No 'this_month' literal appears outside comments.
  • A runtime probe on 17.4.0: parse({}) returns { defaultRange: 'this_month', allowCustomRange: true }. It is repeatable, returns a fresh object each call, and does not throw.

Explicit values are unchanged. The existing test that pins custom still passes, and a new test pins last_7_days together with allowCustomRange: false.

Blast radius is zero. There are exactly three authored dateRange elements, all under examples/schema-catalog, and every one of them sets defaultRange.

Importing a runtime spec schema into core is allowed.

The derivation is robust against objectstack main fc6ddb8, where the wrapping is identical: lazySchema, then strictObject, then an optional dateRange.

  • If the spec dropped .default, the function returns undefined and the dashboard renders unfiltered. That is again the spec's answer.
  • If the spec renamed or removed the arm, the code throws a TypeError on first use. That failure is loud, and both the new core test and the Spec Main Shape Gate would catch it.
  • parse({}) under strictObject has no side effects.

Every entry point shares the change. All readers go through resolveDashboardFilterDefs: DashboardRenderer (two useMemo sites), DashboardFilterBar, and the designer's DashboardWidgetInspector. So the behaviour is uniform.

No new SSR or bundle exposure.

  • Core builds with tsc, sets sideEffects: false, and keeps the spec external.
  • @objectstack/spec/ui was already loaded.
  • lazySchema defers construction until first use.

Tests. vitest could not be re-run in this container, because the root workspace is missing @vitejs/plugin-react. This is an environment problem, not the PR's. The review relies on the dev's run (121 files, 1199 tests) and the dev's ablation (4 exact failures), plus the independent runtime probe above.

② Semver level

'@object-ui/core': minor is correct, because a user-visible behaviour change is at least minor and the fixed group forbids major. The changeset states the behaviour change explicitly. plugin-dashboard changes tests only, so it needs no entry.

③ Boundary flags

  • Runtime behaviour change for downstream consumers. A dashboard authored as dateRange: { field } now opens filtered to the current month, and widget queries receive { $gte: '{current_month_start}', $lte: '{current_month_end}' }. This is the spec-declared arm, and it is declared in the changeset and the docs.
  • Dependency graph. No package.json changes. The only new use is a runtime DashboardSchema value read over an existing edge. The packages/types mirror is deliberately untouched.
  • Dist is untracked.
  • Spec floor. The floor is ^17.3.0, and the default was verified on 17.4.0 and on main. Because the value is derived from whatever spec is installed, the read site cannot diverge from the spec.
  • Reported only, not a contract issue. Commit af43adb carries a model-named Co-Authored-By trailer.

Implemented-by: claude/issue-10339-daterange-default
Reviewed-by: session_01877XiBYSaRCk2CU7cMSg3S

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 24, 2026 19:21
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 24, 2026
Merged via the queue into main with commit 86982ac Sep 24, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10339-daterange-default branch September 24, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(core,plugin-dashboard): a dashboard dateRange that omits defaultRange renders UNFILTERED, but the spec defaults it to this_month

1 participant