Skip to content

fix(plugin-report): stop reading report.filter as an alias for runtimeFilter - #5224

Merged
os-support-ai merged 3 commits into
mainfrom
claude/issue-5137-report-filter-alias
Aug 18, 2026
Merged

fix(plugin-report): stop reading report.filter as an alias for runtimeFilter#5224
os-support-ai merged 3 commits into
mainfrom
claude/issue-5137-report-filter-alias

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Fixes#5137

DatasetReportRenderer read filter as a lenient alias for runtimeFilter — a key the strict ReportSchema in @objectstack/spec already rejects, and rejects while naming the replacement itself:

Unrecognized key(s) on this report: `filter`. Did you mean `filter` -> `runtimeFilter`?

So the producer side was unambiguous and even shipped the migration hint, while the consumer quietly accepted the rejected spelling anyway. Metadata that could not be authored through the validated path still rendered when it arrived by another route, which is the consumer-tolerance shape the declared=enforced doctrine retires. Triage ruled this needs no maintainer decision: the spec has already spoken.

What changed

Both alias reads are gone, in packages/plugin-report/src/DatasetReportRenderer.tsx:

  • the report-level report.runtimeFilter ?? report.filter
  • the per-joined-block block.runtimeFilter ?? block.filter

DatasetReportLike.filter also stops being an undocumented equal-status alias. It stays declared, but now carries a doc comment saying it is not authorable, is never applied, and exists only so the renderer can notice it.

The consequence, and why the key does not go quiet

Removing the read alone would have been a silent narrowing: a stored document still carrying filter now renders unfiltered, which for a scoped report is worse than an error. Server-side permissions are unaffected and still apply, so this is not an access-control hole — it is a report showing rows its author meant to exclude, with nobody told.

So the dropped key is reported rather than applied. When a document carrying filter reaches the renderer, a dev-mode console.warn fires once per offending report or block, naming the key, quoting the spec's own rename hint verbatim, and stating that no filter was applied. It is a no-op under NODE_ENV=production, changes no types, and rejects nothing.

The rename hint was copied from the spec rather than paraphrased — verified by running ReportSchema.safeParse against the pinned @objectstack/spec 17.0.0 and matching the exact substring, including its (U+2192), which the issue body transcribed as ASCII ->. A test pins the two strings together so an author meets one message, not two dialects of one message.

Placement check, since the ruling asked for it: console.warn is this repo's established dev channel for exactly this class of finding (@object-ui/core column-identity conflicts, i18n missing keys, ObjectMap, actionKeys), so the warning does land somewhere a human sees it. Worth stating plainly, though: it reaches a developer or metadata author at the devtools console, not the end user looking at the report. Making the unfiltered render visible in the UI itself would be a larger behaviour change than the ruling authorises, so it was not attempted here.

Tests

packages/plugin-report/src/__tests__/DatasetReportRenderer.rejectedFilterAlias.test.tsx — 9 tests pinning all three required facts:

  1. runtimeFilter still applies, unchanged — report-level and per joined block, and the correct spelling stays silent.
  2. A document carrying only filter applies nothing — asserted on the selection the renderer sends, not merely on the alias value's absence; also that the rejected value does not re-enter the selection under another name, that the report still renders, and that a stored filter cannot shadow the host-supplied runtimeFilter prop.
  3. That case is loud — the warning fires, names the document, the key, the rename and UNFILTERED; it quotes the spec's own hint; it fires once per offending block and does not re-spam across re-renders; and it is silent under NODE_ENV=production while still applying no filter.

Reverse-verified in two legs from the committed fix, each isolating one fact:

  • Leg A — restore the two alias reads, keep the warning: 4 red, exactly the "applies nothing" assertions. The runtimeFilter tests stayed green.
  • Leg B — remove the two warn calls, keep the alias removal: 3 red, exactly the loudness assertions.

Restored after each leg and confirmed byte-identical to the committed fix (git diff HEAD empty).

Verification

Gate union run at e9a7dfb36 (final commit):

  • pnpm exec vitest run packages/plugin-report/13 files, 149 tests passed
  • pnpm --filter @object-ui/plugin-report type-check — clean (tsc --noEmit && tsc -p tsconfig.test.json)
  • pnpm exec eslint on both changed sources — 0 errors (remaining warnings are pre-existing lines this PR does not touch)
  • check:control-bytes, check:phantom-deps, check:self-import — green
  • check-changeset-presence, check-changeset-no-major — green

Test scope was deliberately narrowed to packages/plugin-report; the full farm is left to CI. check:published-dist builds every published package and exceeded the local 10-minute cap, so it too is left to CI. The published surface is unchanged either way — the new resetReportFilterAliasWarnings export (test-only, house precedent from resetColumnIdentityWarnings) is not re-exported from the package barrel.

Changeset

@object-ui/plugin-report: minor, with the break described in the body per AGENTS.md §版本号策略. Deliberately not major — that trips the fixed-group gate and versions all 39 packages.

Scope

Three files: the renderer, its new test, the changeset. Nothing in content/docs (#5047 is the docs half and is docs-only). The rest of the lenient-alias family — #5116, #5120, #5067 and #5068 — is untouched here; those are separate cards with separate consumers and remain open.


Generated by Claude Code

…timeFilter`
The strict `ReportSchema` in `@objectstack/spec` already rejects `filter`
and names the replacement itself, so honouring it in the renderer made the
runtime looser than the published contract. Both alias reads go — the outer
`report.runtimeFilter ?? report.filter` and the per-joined-block twin.
Dropping the read alone would have been a silent narrowing (a stored document
carrying `filter` renders unfiltered), so the key is reported instead of
applied: a dev-mode warn-once that names the key, quotes the spec's own rename
hint verbatim, and says no filter was applied.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)25.3 KB350 KB
Entry fileindex-D4XEdDM3.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)9.83KB3.70KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)8.92KB3.41KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)25.13KB5.40KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.13KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.64KB2.21KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.79KB
auth (createAuthenticatedFetch.js)6.34KB2.43KB
auth (index.js)2.71KB1.22KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.02KB0.88KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)26.07KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.65KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)505.95KB113.30KB
core (index.js)4.11KB1.62KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)159.03KB44.08KB
fields (index.js)237.07KB59.46KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.42KB1.39KB
i18n (pickLocalized.js)3.69KB1.73KB
i18n (provider.js)23.13KB7.63KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)27.60KB6.63KB
i18n (useSafeTranslation.js)7.77KB3.13KB
layout (index.js)39.16KB10.97KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)9.35KB3.31KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.42KB1.42KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.81KB0.83KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.62KB12.83KB
plugin-charts (index.js)64.75KB18.37KB
plugin-chatbot (index.js)181.21KB43.14KB
plugin-dashboard (index.js)127.85KB32.73KB
plugin-designer (index.js)212.39KB42.83KB
plugin-detail (index.js)241.46KB60.56KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)123.77KB30.07KB
plugin-gantt (index.js)164.10KB39.87KB
plugin-grid (index.js)198.22KB53.27KB
plugin-kanban (index.js)52.93KB14.60KB
plugin-list (index.js)111.39KB27.03KB
plugin-map (index.js)20.02KB6.58KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)42.84KB11.77KB
plugin-timeline (index.js)26.68KB7.66KB
plugin-tree (index.js)8.50KB2.88KB
plugin-view (index.js)83.81KB20.49KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)31.56KB10.70KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.33KB0.69KB
react (schema-input.js)1.45KB0.83KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (index.js)4.77KB2.16KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)10.76KB3.17KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)6.92KB2.40KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)3.08KB1.53KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DatasetReportRenderer reads report.filter as a lenient alias for runtimeFilter — a key the strict spec rejects with an explicit rename suggestion

2 participants

@os-support-ai@claude