Skip to content

mergedSort hands table.defaultSort to the delegated list view UNWRAPPED, while the non-grid fetch path wraps the same value — the #5270 arity split, one branch short #6235

Description

@yinlianghui

Found while retiring the direction spelling from toSortItems (#6011, PR #6234) — reading ObjectView's sort chains to decide what the stale fixture at ObjectView.filterSources.test.tsx actually pins. Filed unassigned, no labels — triage grades it. Not fixed in #6234: that card is a spelling retirement on a published export, and this is an arity defect on a different surface.

Line numbers re-derived on claude/issue-6011-tosortitems-direction @ b0d186b79; they sit within one line of main.

The split

ObjectGridSchema.defaultSort is declared a single object{ field: string; order: 'asc' | 'desc' } (packages/types/src/objectql.ts, zod mirror agreeing). Three places in packages/plugin-view/src/ObjectView.tsx resolve it, and two of them account for that arity:

  • non-grid fetch (:791) — wraps it: || (schema.table?.defaultSort ? [schema.table.defaultSort] : undefined), so the shared sink receives the SortConfig[] it declares.
  • grid path (:1375)defaultSort: schema.table?.defaultSort, forwarded into the slot that declares the bare object. Correct by construction; ObjectGrid does its own schemaSort ?? [schema.defaultSort] resolution.
  • delegated path (:1522-1525) — does not:
constmergedSort=currentNamedViewConfig?.sort||activeView?.sort||schema.table?.sort||schema.table?.defaultSort;// <- bare object, unwrapped

mergedSort lands at :1575 as sort: mergedSort on the list-view node the host's renderListView receives — a slot declared string | SortConfig[]. Every branch above the last produces an array (or a string); only the defaultSort fallback produces a bare { field, order }.

The comment immediately above that chain (:1513-1516) asserts the opposite:

Both land on list-view's own filter / sort keys below, so a canonical value arrives in the slot that already matches its shape.

That is true of the first three branches and false of the fourth.

Why this looks familiar

This is the same shape #5270 fixed one branch over: an array/object arity mismatch with no compile-time witness, because ObjectViewSchema.table collapses to a bare index signature (#5102) and the delegated list-view node is assembled with (schema as any).K reads on the host-composition surface (#5097). #5270's own test header warns about exactly the failure this reproduces — a parseSchemaSort-style reader gets a shape it cannot parse, and the symptom is a missing sort with no error, not a crash.

Why no test catches it

packages/plugin-view/src/__tests__/ObjectView.filterSources.test.tsx (forwards the sort alongside it) is the only assertion on this forward, and it is arity-blind in both directions: it passes an already-array defaultSort behind an as any and asserts verbatim toEqual on what came out. A bare-object defaultSort — the declared shape — is never exercised, and even if it were, a verbatim pass-through assertion would report it as passing. #5270's header names this trap by hand: "Pinning the forwarded shape alone would have re-pinned the bug."

(#6234 re-spelled that fixture's directionorder and left its arity alone, deliberately, recording the reason in a comment there. This card is that recorded reason.)

What to decide

Not a dev's own call — it is which of two contracts the delegated sort slot is meant to carry:

  1. Wrap at mergedSort, matching :791 verbatim: || (schema.table?.defaultSort ? [schema.table.defaultSort] : undefined). One line, and it makes all three consumers agree. Note this is the resolution ObjectGrid already performs for the same pair, so it introduces no fourth dialect.
  2. Leave it and correct the comment — if a host's renderListView is genuinely expected to accept the deprecated bare-object shape, then the slot's declared type is what is wrong, and the claim at :1513-1516 needs to stop asserting canonicality.
  3. Something upstreamtable.defaultSort is @deprecated Use sort instead; a route that stops resolving it on new surfaces may be preferable to teaching a fourth reader about it.

Recommendation is 1, on the grounds that :791 already ruled the wrapping question for this exact pair and a maintainer ruling of 2026-08-22 (recorded in the comment at :779-784) explicitly rejected widening the shared sink to guess at a bare { field, order } — so the wrap belongs in the caller, which is where :791 put it. But the delegated surface is host-facing, so whether an out-of-tree renderListView is already coping with the bare object is not derivable from this repo.

Whatever is chosen, the fixture above should gain a case that passes the declared object arity, since the current one cannot fail either way.

Dedup

Searched open issues for mergedSort, defaultSort + renderListView, and the arity wording — nothing on this. Related: #5270 (the arity ruling this extends), #5102 / #5097 (why there is no compile-time witness), #6011 / PR #6234 (where it was found).

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions