Skip to content

fix(plugin-timeline): refuse a gantt row that is not an object - #10351

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-7364-timeline-nonobject-row
Sep 24, 2026
Merged

os-project-manager merged 1 commit into
mainfrom
claude/issue-7364-timeline-nonobject-row

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #7364
Clause-②: no

What this does

classifyGanttRows (plugin-timeline renderer.tsx) now refuses every gantt ROW that is not an object, not only null. A row that is a number, a string, a boolean or an array (items: [0], ['x'], [true], [[]]) used to draw an empty, unlabelled row with no word to the author. It is now refused through the EXISTING timeline.gantt.unusableRange.malformedRow key, naming the path and value, for example items[0] is 0, which is not a row shape and items[0] is an array, which is not a row shape. No new key, no locale pack edit, no change to validate. The renderer now agrees with validate, which already refused these shapes at authoring time.

"Not an object" is typeof row !== 'object' || row === null || Array.isArray(row): the set JSON can spell and the zod mirror (z.object) refuses. No prototype test is made (a Date, class instance or Proxy row cannot be written in JSON; the live-Proxy row control keeps drawing).

This deliberately supersedes one clause of the objectui#7164 A+ ruling

Ruling: objectui#7364 comment 5809218505, UI closure batch 2 item 1, letter A, maintainer 「同意」. The #7164 A+ ruling required the control rows items: [0] and items: [[]] to keep DRAWING, and they were pinned as controls. We are overriding that clause of the A+ ruling on the maintainer's word above. The control assertions were not quietly "made green": each is rewritten to expect the refusal, with the ruling id stated beside it, and none is deleted:

  • timeline-gantt-malformed-row-7164.test.tsx: the two control rows are removed from the DRAWS table with a comment pointing at the new block; a new block a row that is NOT AN OBJECT is refused — objectui#7364 supersedes the #7164 control pins [0], [[]], ['x'], [true] as REFUSED at items[0] (diagnostic text, row clause, not the date clause, zero bars, empty axis, role="alert"), a non-object row beside good rows (items[1] is 0, wins over a later bad date), and a control that well-formed object rows (1 and 2 rows), a row with no items key and a row whose items is null still DRAW. The file header records the supersession.
  • timeline-gantt-date-brand-7027.test.tsx pin 6: the same two former controls move out of the DRAWN list and are asserted as refusals, with the reason.
  • packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts: header comment only. It said [0] and [[]] "still DRAW in the renderer", which this change makes false; corrected. No assertion changed.

One exclusion site moved (stated, not hidden)

Array.isArray(row) now runs before U3 reads row.items, so a revoked Proxy handed as a ROW dies at IsArray rather than get. That is the same move objectui#7164 already made for items and a row's items: same exclusion (JSON cannot spell a proxy), the site moved one operation up inside the same function. The pin 5 row in timeline-gantt-date-brand-7027.test.tsx is updated with that reason, and the classifyGanttRows docblock says so.

Verification (head 9e09349a9)

  • pnpm exec vitest run --maxWorkers=2 packages/plugin-timeline/ packages/types/src/__tests__/timeline-items-row-shape-7164.test.ts (repo root, under the shared verify lock): Test Files 28 passed (28), Tests 353 passed (353).
  • Reverse verification, from the committed fix: the predicate mutated back to row == null (mutant marker count 1, fix marker count 0 on disk), then the two edited test files run: Tests 7 failed | 55 passed (62). Red: the four non-object rows, the beside-good-rows case, the revoked-Proxy-as-ROW site pin, and pin 6's control block. The well-formed row controls stayed green. Restored with git checkout HEAD --; git diff HEAD empty and the on-disk blob hash equals the HEAD blob hash.
  • turbo run build --filter=@object-ui/plugin-timeline^... (10/10) then plugin-timeline type-check (tsc --noEmit && tsc -p tsconfig.test.json, which lists both edited test files): exit 0.
  • ESLint --no-inline-config on the four touched source/test files: 0 errors (17 pre-existing warnings, none on changed lines).
  • check:new-line-citations (0 new), check:control-bytes, check:changeset-claims, check:pending-changeset-literals, check:test-path-roots, check-changeset-presence.mjs, check-changeset-no-major.mjs: all exit 0.
  • Repo-wide pnpm lint / full pnpm test shards: declared to CI, not run locally.

Acceptance notes

Session: https://claude.ai/code/session_01D486C1axmXnrkJMNUfz2eb


Generated by Claude Code

classifyGanttRows now refuses every row that is not a plain object (null,
a number, a string, a boolean, an array) through the existing
timeline.gantt.unusableRange.malformedRow key, instead of drawing an empty,
unlabelled row. The renderer now agrees with validate, which already refused
these shapes at authoring time. No new key and no pack strings.

This deliberately supersedes the objectui#7164 A+ clause that kept
items: [0] and items: [[]] drawing; those control pins are rewritten to
expect the refusal, with the ruling id (5809218505) as their reason.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3041.8 KB 3104.5 KB
Main entry chunk (gzip) 148.3 KB 350 KB
Entry file index-tFhq7bV0.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) 27.95KB 10.04KB
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.52KB 3.45KB
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) 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.42KB 129.43KB
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.70KB 64.09KB
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.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 34.99KB 11.45KB
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) 16.08KB 3.95KB
plugin-calendar (index.js) 51.44KB 14.62KB
plugin-charts (index.js) 71.84KB 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) 168.31KB 41.45KB
plugin-grid (index.js) 215.46KB 58.88KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 114.13KB 28.14KB
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.72KB 8.96KB
plugin-tree (index.js) 10.56KB 3.71KB
plugin-view (index.js) 85.79KB 21.35KB
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

@os-project-manager
os-project-manager marked this pull request as ready for review September 24, 2026 20:03
@os-project-manager
os-project-manager added this pull request to the merge queue Sep 24, 2026
Merged via the queue into main with commit 0b6b295 Sep 24, 2026
45 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7364-timeline-nonobject-row branch September 24, 2026 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants