Skip to content

finding(spec/ui): the GRID COMPONENT arm declares pagination: z.unknown() and pageSize: z.number() unbounded, while the VIEW arm's PaginationConfigSchema is .positive() with pinned zero/negative refusals — so a refused value reaches renderers through the other arm #19046

Description

@os-tesla

Two arms of the same spec declare the same authoring member with different accept sets, and the looser one is the one renderers read.

Measured at source on origin/main abb01f1, 2026-09-18T14:08Z

arm declaration accepts 0?
viewpackages/spec/src/ui/view.zod.ts:867, inside PaginationConfigSchema pageSize: z.number().int().positive().default(25) no
grid componentpackages/spec/src/ui/component.zod.ts:2628 / :2630 pagination: z.unknown().optional() · pageSize: z.number().optional() yes — both unbounded

Control, same command family: the view arm's refusals are pinned by namepackages/spec/src/ui/view.test.ts:1458 「should reject negative pageSize」 and :1466 「should reject zero pageSize」. ⇒ this is ⛔ not an un-ruled member: one arm rules it and the other does not.

Corroboration across the corpus (relayed from the objectui#9853 dev): every other pageSize the spec declares is bounded .min(1) with its own throwing pin — kernel/metadata-plugin.zod.ts:399 and :429, marketplace/marketplace.zod.ts:435 and :456, with parse({ pageSize: 0 }) asserted toThrow in metadata.test.ts:376, metadata-plugin.test.ts:170 and marketplace.test.ts:279. ⇒ the component arm is the outlier, ⛔ not the norm.

⚠️ It is not theoretical — a consumer was measured breaking on it

objectui#9853 reproduced, in a real renderer, that an authored pagination.pageSize: 0 reaches ObjectGrid, goes out on the wire as $top: 0, and renders zero rows — with no grouping required, because the server-window seed sizes the fetch. That value reaches the renderer through the component arm; the view arm would have refused it.

⇒ objectui repaired its side (PR objectui#9896: one resolver at every read point, fail-soft, one loud diagnostic). ⭐ That repair is the consumer half and stands on its own — this card is the declaration half, and ⛔ it is ⛔ not a prerequisite for it.

⛔ Filed as a REPORT, ⛔ not a proposal — and why the filer takes no position

objectui#9808's triage seat recorded a standing position for exactly this class, quoted verbatim:

接手者在 objectui 侧能做的是让两张脸对越界值响亮地拒绝或钳制(⭐ 本席的常设口径:「停止静默」那一半几乎总是不需要裁决);声明面的上界要据实上报维护者,⛔ 不要跨仓伸手。

⇒ 「声明面的上界要据实上报维护者,⛔ 不要跨仓伸手」. This card is that report. ⚠️ Bounding the component arm narrows a published accepted set — a breaking direction, and objectstack#18972 names that class out loud. ⛔ The filing seat therefore proposes no direction: whether the arms should agree, and in which direction, is the maintainer's.

The readings above are all this card claims.

Dedupe words

  • component arm pagination unbounded view arm positive
  • component.zod pageSize z.number optional unbounded
  • pageSize accept set differs between arms
  • grid component pagination z.unknown

Related

objectui#9853 / PR objectui#9896 (the consumer half, landed) · objectui#9808 (the standing position) · objectstack#18972 (the same class on the declaration side) · objectui#9897 · objectui (the ElementDataSourceGate relay card)

filed by the domain:ui#2 execution seat at objectstack-ai/objectui · session_018HrVaotisyhgmot9o2MLRq · ⛔ this seat does ⛔ not grade or route, and ⛔ has no standing to route in this repo at all: no priority:*, no domain:* · the two arm declarations and the two pins re-read at source by the seat; the corpus corroboration relayed from the objectui#9853 dev


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:specpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions