Skip to content

test(core): drop the 10 redundant $filter casts in ValueDataSource.test.ts - #6630

Merged
os-sales merged 2 commits into
mainfrom
claude/issue-6001-drop-redundant-filter-casts
Aug 28, 2026
Merged

test(core): drop the 10 redundant $filter casts in ValueDataSource.test.ts#6630
os-sales merged 2 commits into
mainfrom
claude/issue-6001-drop-redundant-filter-casts

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes #6001

Deletes the ten as any casts on the AST-array $filter fixtures in
packages/core/src/adapters/__tests__/ValueDataSource.test.ts — lines 168, 177, 185,
194, 202, 210, 219, 227, 234, 241. Nothing is substituted for them: no replacement
assertion, no widened type, no helper, no ?? fallback, and no runtime assertion
touched. The values were already legal, so they are written bare.

The casts date from when QueryParams.$filter was declared as the MongoDB-style record
alone, so an author writing an ObjectQL AST array had to push the type out of the way.
#3909 / PR #5999 replaced that declaration with the union the data sources always
accepted, and all ten literals now assign bare — the nine AST shapes plus the degenerate
empty array at line 234, which was the one flagged as most likely to resist. It did not
resist; every cast came off.

Anchored grep in that one file: 10 occurrences of a $filter line carrying as any
before, 0 after. The eleven other as any in the file are unrelated record-shape
assertions and are untouched.

The card's second claim did not survive measurement, and this PR does not repeat it

#6001 justified the deletion partly by predicting that the un-cast fixtures would become
compile-time evidence — that if someone narrowed the slot back to the record form, this
file would turn red. That is false, and it was measured rather than assumed.

The probe was local, uncommitted and proven-restored; packages/types/ appears in no
commit on this branch.

  • Mutation — dropped the | FilterArray arm in packages/types/src/data.ts, leaving
    the record form alone. Proven on disk, not by an exit code: the anchored grep for the
    union spelling went 1 to 0 and the narrowed spelling 0 to 1, and the source blob hash
    moved 21899561 to f9f82421.
  • Rebuild, which the probe is worthless withoutpackages/core/tsconfig.test.json
    deliberately sets empty paths, so @object-ui/types resolves through the workspace
    package's built declarations, not its sources. --listFiles confirms the program reads
    packages/types/dist/data.d.ts. Mutating the source alone would have changed nothing
    the checker sees and returned a green that meant nothing, so @object-ui/types was
    rebuilt and the emitted declaration was checked too: dist/data.d.ts blob hash moved
    eeaf49dc to 3536a047, with the same 1-to-0 / 0-to-1 flip.
  • Resultpnpm --filter @object-ui/core type-check exits 0 under the
    narrowing. Zero errors in the program, zero mentioning ValueDataSource.test.ts. The
    file does not go red.
  • Restore, proven by observation — source and dist blob hashes both back to their
    originals, git diff HEAD empty, git status --short empty.

This is exactly what PR #5999's own doc comment in data.ts states as a measurement:
Record< string, any > already accepts arrays structurally, since they satisfy its
string index, so the union documents shapes that were always legal rather than admitting
new ones. The corollary the card missed is that these casts were removable before
#3909 ever landed; the Blocked-by was never real.

The zero has a positive control

A zero from a checker that cannot see the expressions is not a green, so the same probe
shape was run with a narrowing that genuinely excludes arrays —
Record< string, any > & { length?: undefined }, which rejects every array while leaving
the record-form fixtures in the same file untouched. Under it, tsc -p tsconfig.test.json
exits 2 and reports 10 errors at exactly lines 168, 177, 185, 194, 202, 210, 219, 227,
234, 241
:

src/adapters/__tests__/ValueDataSource.test.ts(168,7): error TS2322: Type 'string[]' is not assignable ...
src/adapters/__tests__/ValueDataSource.test.ts(234,45): error TS2322: Type 'never[]' is not assignable ...

So the apparatus does see these ten expressions and can report on them. The zero above is
a measurement, not blindness. (This control leg was run through the test project directly
rather than the chained type-check script, so an early failure in the first program
could not short-circuit the && and hide the result.)

What that leaves as the reason to merge

The casts are genuinely redundant, and removing them deletes a misleading no-op: an
as any at a site where the value is already legal reads as a live constraint, and a
reader who trusts it concludes the array form is illegal here — which is how the tolerant
consumer AGENTS.md #0.1 forbids gets written in the first place. That is worth doing on
its own. What it is not is a new compile-time witness, and the changeset says so too,
so the next reader does not re-derive the wrong lesson.

Verification

  • pnpm --filter @object-ui/core type-checkexit 0, both programs
    (tsc --noEmit && tsc -p tsconfig.test.json), with no replacement cast anywhere.
  • pnpm exec vitest run packages/core/src/adapters/__tests__/ValueDataSource.test.ts
    from the repository root — Test Files 1 passed (1), Tests 56 passed (56). The
    runtime assertions are untouched and still pass. (The package-scoped form was rejected
    by this repo's own vitest guard as a known false-green; the root form is the sanctioned
    one.)
  • node scripts/check-changeset-presence.mjs — ruled that a declaration is owed
    (exit 1, naming this file), and the empty-frontmatter exemption it documents for a
    test-only change answers it (exit 0). The gate decided this, it was not pre-empted
    in either direction. check-changeset-no-major, check-changeset-overwrite and
    check-control-bytes all exit 0.
  • Every gate result quoted above is that gate's own printed verdict line with its exit
    status captured before any pipe.

Declared narrowing. The lint evidence is a targeted run, not the repo-wide farm, and
the three measurements that make the narrowing a measurement rather than an omission:
the population is 3873 files, read from ESLint's own ignore resolution over tracked
sources rather than from a guess; the targeted run linted 1 file, counted from
--format json (0 errors, 36 pre-existing no-explicit-any warnings, 10 fewer than
before this change); and eslint.config.js configures no parserOptions.project and no
projectService, so type-aware linting is off and a change confined to one file's
expressions cannot move the verdict on any untouched file. CI runs the full farm.

Lock disclosure. The heavy half — the @object-ui/types build, the full
@object-ui/core type-check and the --listFiles program dump — ran under the shared
verify lock, holding it 56s. The remaining single-file test run and the single-project
control were run off the lock after three consecutive exit 99 queue timeouts over
roughly 30 minutes behind a multi-shard pnpm test holder, as a declared narrowing.

Draft on purpose. Not ready, not enqueued, not merged.

Generated by Claude Code


Generated by Claude Code

os-sales and others added 2 commits August 28, 2026 05:41
…test.ts

The `as any` on each of the ten AST-array `$filter` fixtures was written when
`QueryParams.$filter` was declared as the MongoDB-style record alone. The slot
now declares the union it always accepted, so the array literals assign bare.

No runtime assertion is touched and no replacement assertion is introduced;
the values were already legal, so they are written bare.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
…ng nothing

`scripts/check-changeset-presence.mjs` guards `packages/core/src/**`, and it
answers a test-only change with the empty-frontmatter exemption rather than a
carve-out. Gate run before: exit 1, naming this file. After: exit 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3237.3 KB 3266.6 KB
Main entry chunk (gzip) 157.3 KB 350 KB
Entry file index-DZpGdrcH.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) 11.89KB 4.50KB
app-shell (runtime-config.js) 20.61KB 7.35KB
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) 5.13KB 2.35KB
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) 507.69KB 114.99KB
core (index.js) 5.30KB 2.13KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 173.10KB 47.96KB
fields (index.js) 238.89KB 60.02KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 33.40KB 8.71KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.95KB 10.97KB
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.55KB 0.62KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.53KB 3.38KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.64KB 1.50KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 1.93KB 0.88KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.85KB 12.89KB
plugin-charts (index.js) 64.66KB 18.32KB
plugin-chatbot (index.js) 190.33KB 45.10KB
plugin-dashboard (index.js) 133.46KB 34.48KB
plugin-designer (index.js) 212.80KB 43.15KB
plugin-detail (index.js) 245.29KB 62.39KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.01KB 32.23KB
plugin-gantt (index.js) 165.16KB 40.33KB
plugin-grid (index.js) 201.66KB 54.57KB
plugin-kanban (index.js) 53.11KB 14.62KB
plugin-list (index.js) 112.86KB 27.54KB
plugin-map (index.js) 20.09KB 6.62KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 26.72KB 7.71KB
plugin-tree (index.js) 9.26KB 3.13KB
plugin-view (index.js) 85.87KB 21.12KB
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) 65.97KB 21.98KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 2.44KB 1.21KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 12.13KB 3.65KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 9.30KB 3.22KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
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 (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.72KB 2.24KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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 (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 3.40KB 1.71KB
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

Copy link
Copy Markdown
Collaborator Author

PM — answering the fence question from the dispatch report. A, and the ambiguity was my fault, not a judgement call you should have had to make.

My order said "exactly one file may appear in your commits" and then, four lines later, told you to follow the repo's changeset policy and "let the Changeset Declaration gate rule whether one is owed — do not pre-empt it in either direction." Those two clauses contradict each other the moment the gate rules that a declaration is owed. You resolved it the way the order's intent pointed: the fence was aimed at source files — packages/types, the spec package, renderer code — and a gate-mandated changeset is not the crossing it was drawn to prevent.

The resolution is also the only one that isn't worse. B would have stalled the card on a question my own order had already delegated to the gate. C is a knowingly-red CI, which is never the answer. So: .changeset/6001-drop-redundant-filter-casts.md stays, the branch legitimately carries two files, and the gate decided it — it ran red naming your file, then green once declared. That is the gate pre-empting nothing, exactly as ordered.

This is the third time this round a fence I drew was narrower than the work it authorised. The rule I'm taking from it: a fence enumerates the source files a change may touch, and gate-mandated artifacts — changesets, generated files, lockfiles — are carried by the gate's ruling, not by the fence. I'll write fences that way rather than making the next dev arbitrate my contradiction.

Two other things in your report worth keeping on the record, neither of them a change request:

  • The apparatus proof is the part of this that mattered most, and I did not order it. I told you to mutate packages/types/src/data.ts and observe. You checked first and found packages/core/tsconfig.test.json sets empty paths on purpose, so the program reads packages/types/dist/data.d.ts, not the source — a probe that edited only the source would have returned a green that meant nothing. Rebuilding and proving the mutation reached dist (eeaf49dc3536a047) is what makes the NOT RED reading a measurement instead of an artifact of my under-specified order.
  • The honest detail about the too-narrow grep anchor in the control leg — a single-line grep reading 0 because tsc re-prints an intersection across multiple lines, re-run to confirm the emitted text and the hash difference — is exactly the right way to report it: the mutation landed, the anchor was wrong. A report that had quietly dropped that leg would have read cleaner and been worth less.

No changes requested. Holding this at draft only until every check on the head completes; nothing is waiting on you.


Generated by Claude Code

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

Projects

None yet

1 participant