Skip to content

fix(plugin-grid): import type inference requires a date shape before Date.parse (objectui#11813) - #11881

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11813-import-date-inference
Oct 8, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11813-import-date-inference

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11813

Clause-②: no. An internal predicate gets stricter. No export, prop, @object-ui/types member or accepted input changes; inferColumnType keeps its signature and returns text where it wrongly returned date.

What changed

isDateLike in packages/plugin-grid/src/importParsers.ts was !isNumberLike(s) && !isNaN(Date.parse(s)). V8's legacy Date.parse skips unknown words before the first number and reads any word whose first three letters spell a month. Measured in Node 22.22.0:

value Date.parse reads it as
Phase 2 2001-02-01
Building 7 2001-07-01
Room 12 2001-12-01
Imported QA task 1 2001-01-01
Marketing 2026 2026-03-01
Room 101, Oct 0101-10-01
A-12 2001-12-01
1.2.3 2003-01-02

A value now needs an explicit date shape before Date.parse is asked; Date.parse then only confirms the parts are in range. The shapes:

  • Numeric, year first: 2026-10-07, 2026/10/07, 2026.10.07, and ISO-8601 date-times (2026-10-07T12:00:00Z, 2024-12-31 09:00, 2026-10-07 12:00:00 GMT+0800).
  • Numeric, day and month first: a two- or four-digit year, and the same separator twice: 10/07/2026, 7.10.2026, 10/7/26, optionally with a clock time (10/07/2026 2:30 PM).
  • ISO-8601 year-month: 2026-10.
  • English month-name forms: a digit, a month name or abbreviation, and no word that is not a month, weekday, AM/PM or zone word: Oct 7, 2026, 7 October 2026, 7-Oct-2026, Wed, 07 Oct 2026 12:00:00 GMT. Words are read with \p{L} and the u flag, per the repo's i18n regex rule.

Only the predicate changed, inside the claim's fence (comment 6051515735). inferColumnType's signature, isNumberLike, the mapping-hint scoring, the record conversion and ImportWizard.tsx are untouched. Changeset: .changeset/11813-import-date-inference.md, @object-ui/plugin-grid patch.

Pins

In packages/plugin-grid/src/importParsers.test.ts, describe inferColumnType: text ending in a number is not a date (objectui#11813):

  • Phase 2, Building 7, Room 12 and Imported QA task 1 infer as text, singly and as a column. So do Marketing 2026, Room 101, Oct, A-12, task-1 and 1.2.3.
  • Through suggestColumnMappings, a Task title column of Imported QA task 1 and Imported QA task 2 carries inferredType: 'text' and maps onto a Title text field. On main it carried date. The type gate then halved the soft match below the auto-apply threshold, so the column was not mapped at all.
  • Control: 2026-10-07, 2026/10/07, 2026-10, 10/07/2026, 7.10.2026, 10/7/26, Oct 7, 2026, 7 October 2026, 7-Oct-2026 and Tuesday, October 7, 2026 infer as date. 2026-10-07T12:00:00Z, 2024-03-14T08:30:00.000Z (the ISO form the xlsx parser emits), 2026-10-07 12:00:00 GMT+0800, 10/07/2026 2:30 PM and Wed, 07 Oct 2026 12:00:00 GMT infer as datetime. Both hold before and after the fix.

Reverse check

The fix was committed first (9e7eedf, then d9f6c6f). Then importParsers.ts was overwritten on disk with its base cef0eee blob: hasDateShape counted 0 and the old one-line isDateLike counted 1. The pins were run, and the file was restored with git checkout HEAD -- PATH. The restore was proven by blob-hash equality with HEAD (cc0bbf0d) and an empty git diff HEAD.

  • Reverted: Tests 11 failed | 46 passed (57). Every failure is expected 'date' to be 'text', the predicted direction. All 16 control cases stayed green.
  • Fixed: Tests 57 passed (57).

Gates

The union ran on dfeee89, the merge of origin/main at 6d8bf0c into this branch.

command exit verdict
pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-grid^...' build 0 lock VERDICT command-exit 0; Scope: 14 of 47 workspace projects
pnpm --filter @object-ui/plugin-grid type-check 0 echoed tsc --noEmit && tsc -p tsconfig.test.json; --listFiles on the test project lists importParsers.test.ts
pnpm exec vitest run --maxWorkers=2 packages/plugin-grid/ 0 Test Files 188 passed (188), Tests 1763 passed (1763)
pnpm check:control-bytes 0 check-control-bytes: OK
pnpm check:new-line-citations 0 0 new citation(s), enforcement report-only
pnpm check:changeset-claims 0 No pending changeset names a file this change touches.
pnpm check:pending-changeset-literals 0 No test source names a pending changeset.
pnpm check:test-path-roots 0 check-test-path-roots: OK
node scripts/check-changeset-presence.mjs 0 2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)
node scripts/check-changeset-no-major.mjs, -fixed.mjs, -overwrite.mjs 0 each prints its ✅ line
pnpm exec eslint --no-inline-config --format json on the two touched .ts files 0 2 files, 0 errors, 0 warnings
  • Guard tests: the glob *.guard.test.* matches no file in plugin-grid. The three *Guard.test.tsx files (importLegacyReferenceGuard, useRowColor.prototypeGuard, RowActionMenu.emptyGuard) are among the 188 files of the package run.
  • Lint narrowing: the population is read from the root lint script, eslint . --no-inline-config, which CI runs. The file count, 2, is read from the --format json output. Invariance: eslint.config.js sets no parserOptions.project or projectService, so linting is not type-aware, and no rule under eslint-rules/ reads the filesystem. This diff therefore cannot move a verdict on an untouched file.
  • NOT MEASURED: check:eager-closure. Reason: it reads apps/console/dist/eager-closure.json, which only a full console build writes; that is left to CI. plugin-grid is eager, because apps/console/src/register-plugins.ts imports it, and its chunk has no per-chunk ceiling. A standalone esbuild-minified gzip of importParsers.ts grows from 2,977 to 3,326 bytes (+349).

Acceptance notes

  • Behaviour change, declared in the changeset. A month name in another language no longer reads as a date. V8 accepted some by their first three letters (7 octobre 2026, 7 Januar 2026, Mars 7, 2026) and never accepted others (7 juin 2026, 7 März 2026). 2026年10月7日 is NaN to Date.parse and stays text. Reading localized date forms would be a new capability, not this card.
  • H3 (locale order). importParsers.ts has no locale rule. V8 reads 07/10/2026 month-first (July 10), and that is unchanged.
  • Correction to the dispatch's control list. 2026-10-07T12:00:00Z infers as datetime, not date, both on main and after (hasTimeComponent). The pin says datetime.
  • Same family, not filed: the wizard's client value check. validateValue in ImportWizard.tsx accepts any value Date.parse reads for a date / datetime field, so Phase 2 mapped onto a date field is not marked red in the preview. Reach not measured. Carrier: objectui#11814 (that file's next card, the preview check). isDateLike stays module-private per the fence.
  • Observation, not filed. ObjectGrid.tsx's own name-based inferColumnType runs a Date.parse sample check whose two branches both return date, so the check decides nothing. Carrier: none.
  • main moved after the merge: 31add5f adds three commits, all in packages/app-shell and .changeset/, disjoint from this diff. They were not merged in.

Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z (os-dev for seat domain:ui#1, claim 6051515735).


Generated by Claude Code

claude added 4 commits October 8, 2026 03:29
…Date.parse (objectui#11813)

isDateLike trusted V8's lenient Date.parse, which skips unknown words before
the first number and reads any word starting with a month's three letters, so
"Phase 2", "Building 7", "Room 12" and "Imported QA task 1" inferred as Date
and drew a "Looks like Date" hint on the mapping step. A value now needs an
explicit date shape first (numeric year-first or day/month-first, ISO-8601
date-time, ISO year-month, or an English month-name form with no other word);
Date.parse then only confirms the parts are in range.

Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z
Co-authored-by: Claude <noreply@anthropic.com>
…atch type gate (objectui#11813)

Claude-Session: https://claude.ai/code/session_01DrKzdPdyLLBW3qpZ4vtk7z
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 333 chunks) 3350.0 KB 3363.9 KB
Main entry chunk (gzip) 86.0 KB 350 KB
Entry file index-D3SswQnL.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) 17.82KB 6.58KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
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) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 584.59KB 140.69KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 238.60KB 66.51KB
fields (index.js) 266.88KB 67.46KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 38.37KB 10.31KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
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.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
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) 16.04KB 3.92KB
plugin-calendar (index.js) 53.39KB 15.52KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 199.63KB 47.46KB
plugin-dashboard (index.js) 144.82KB 39.17KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 176.62KB 45.75KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 240.73KB 66.31KB
plugin-kanban (index.js) 52.17KB 16.37KB
plugin-list (index.js) 117.42KB 29.32KB
plugin-map (index.js) 25.60KB 8.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.07KB 22.93KB
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.81KB 3.58KB
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) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 1.13KB 0.65KB
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) 5.78KB 2.70KB
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 (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
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

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.

import: a text column whose values end in a number ("Imported QA task 1") is inferred as Date, because type inference trusts Date.parse

2 participants