Skip to content

feat(spec): element:record_picker 补声明扁平 sort / limit(#6276 · A 案) - #6624

Merged
qq9340100 merged 3 commits into
mainfrom
claude/issue-6276-record-picker-sort-limit
Aug 8, 2026
Merged

feat(spec): element:record_picker 补声明扁平 sort / limit(#6276 · A 案)#6624
qq9340100 merged 3 commits into
mainfrom
claude/issue-6276-record-picker-sort-limit

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Fixes#6276

裁决依据

维护者 2026-08-08 在 issue 上裁定 A 案裁决评论):补声明 sort / limitelement:record_picker 的四个扁平简写全部成为契约。理据是 #5775 的同一条判例 —— 以被兑现的形状为准

同时裁决明确:B 案不废弃,升格为方向性议题另立 #6590 挂 v18 评估(dataSource 唯一入口 + 扁平简写族整体退役)。A 不堵 B —— 届时 sort/limitobject/filter 一起走 ADR-0087,这两个键是一起退役的,不是新增的债。

前提复核(已核实,成立)

objectui HEAD 7894432packages/components/src/renderers/basic/record-picker.tsx 仍是四键同一模式,dataSource 优先、扁平简写兜底:

constobject=ds.object??props.object;constfilter=ds.filter??props.filter;constsort=ds.sort??props.sort;constlimit=ds.limit??props.limit??50;

本仓 origin/main 上(#5775 落地后)object / filter 已声明,sort / limit 未声明 —— 同一个 renderer 的同一组兜底,一半是契约、一半是暗门。一个照着 object/filter 写法推断出 properties.limit: 20 的作者,拿到的是 renderer 默认的 50 条,零诊断:键在任何东西读到它之前就被 strip 掉了,#5068 闸门把它报成未声明键,值检查从未运行。这正是 ADR-0078 的形状,出现在 #5775 刚刚为此重写过的那个 element 上。

改动

形状对齐 dataSource 内的同名键,而且是从同一份源导入而非另抄一遍 —— 它们是同一份契约的第二种拼写,形状分叉就会变成第三种 sort 方言(strictness ledger 的 report.zod.ts 行已经记了三种):

sort: z.array(SortItemSchema).optional()// 与 ElementDataSourceSchema.sort 同源
limit: z.number().int().positive().optional()

describe() 写明兜底次序:dataSource.sort / dataSource.limit 在两者都写时优先 —— 声明只是把既有优先级写进契约,不改变它。

renderer 的 ?? 50 刻意不做成 schema default。.default(50) 会让每一次 parse 都物化出一个 limit: 50,把「没写」变成「写了」,而且从此要靠人手和 objectui 保持同步。它是 renderer 的兜底,文档里写清楚,声明里不写。

测试与验证

packages/spec/src/ui/component.test.ts(6 条)—— 保留(不被 strip)、值判决(非法 limit / 非法 sort 按名拒绝)、ElementDataSourceSchema 的形状一致性(同一个值两个门 parse 结果相同、同一个非法值两个门判决相同)、以及「不 default」这一条。

packages/lint/src/validate-component-props.test.ts(2 条)—— #5068 闸门的另一半:两个键不再是 unknown-key 发现,且一个错的 limit 现在被判值而不是被静默 strip。

逆向验证(方向先判后跑)。 预判:这是 additive 声明落在 strip 模式的 z.object() 上,撤回后 sort/limit 会被静默 strip,所以保留类与拒绝类断言应当全红;唯一例外是「不 default limit」那条 —— 撤回前后 limit 都是 undefined,它是控制项,本就无法区分方向。实测与预判一致:

spec × retains the flat `sort` shorthand AssertionError: expected undefined to deeply equal [ { field: 'created_at', …(1) } ]
× retains the flat `limit` shorthand AssertionError: expected undefined to be 20
× rejects a non-integer / non-positive `limit` by name expected [Function] to throw an error
× rejects a malformed `sort` by name expected [Function] to throw an error
× parses `sort` / `limit` identically to `dataSource`
Tests 5 failed | 136 passed (141) ← 第 6 条控制项如预判保持绿
lint × reports nothing on the flat `sort` / `limit` shorthands
AssertionError: expected [ { severity: 'warning', …(5) }, …(1) ] to deeply equal []
× now judges the VALUE of a flat `limit` instead of stripping it
AssertionError: expected [] to deeply equal [ Array(1) ]
Tests 2 failed | 22 passed (24)

一条计划外的读数,值得记下来: 撤回声明后 gen:schema 自己就红了 —— authorable-surface 的删除闸门报 2 authorable key(s) disappeared from the contract。也就是说这两个键从落地这一刻起就被闸门锚住了:将来 #6590 真要退役它们,只能走 tombstone + D2 conversion + major changeset,不可能被谁顺手删掉。

生成物与闸门

结果
pnpm --filter @objectstack/spec test343 files / 8814 passed
pnpm --filter @objectstack/lint test62 files / 1594 passed, 4 skipped
typecheck(spec + lint)通过(含 check:scripts-typecheck / check:test-typecheck
check:generated10/10 up to date
check:spec-parsed-aliasOK(1443 bare aliases / 749 pinned / 694 paired)
pnpm lint(ESLint)无输出(通过)
check:nul-bytes / doc-authoring / adr-anchors / empty-changeset / quick-reference-counts / role-word / docs-audit-scope全 OK

生成物只动了两行:packages/spec/authorable-surface/ui.json 加两个键、content/docs/references/ui/component.mdx 加两行表格。

ADR-0122: 复用已有具名 schema SortItemSchema,未产生新具名 schema,计数不动;check:spec-parsed-alias 绿。两个键都无 .default() / .transform(),不影响任何 isomorphism pin。

changeset:@objectstack/specminor(additive 声明,此前 parse 通过的一切仍然通过)。

合入 main 时的一处联合失效(已修,记录在案)

merge origin/maincontent/docs/references/ui/component.mdx 出现第二处 diff:main 的 inline-shape depth budget 改了嵌套形状的渲染方式,我 merge 前生成的产物和 main 的生成器文本合并干净、语义联合错误PageTabsProps.items[].visibleWhen 我这边展开、main 那边收敛)。已按合并后的生成器重新生成,现在对 main 的 delta 恰好只有本 PR 新增的两行。

另有一处 §9 陈旧产物:merge 后只重建了 spec,packages/lint 的 4 条 validate-expressions 用例(#6290/#6584)报红,重建 @objectstack/formula 后全绿 —— 不是本改动引起的,记录以免下一位重新诊断。

范围

⛔ 未动 objectui renderer(它已兑现)。⛔ 未动其它 element(B 案范围,见 #6590)。⛔ 未动 content/docs/releases/


Generated by Claude Code

…rthands (#6276)
The picker's renderer resolves its query from four keys through one identical
`ds.<k> ?? props.<k>` pattern (objectui `record-picker.tsx`):
const object = ds.object ?? props.object;
const filter = ds.filter ?? props.filter;
const sort = ds.sort ?? props.sort;
const limit = ds.limit ?? props.limit ?? 50;
After #5775 the first two flat spellings were declared and the last two were
not, so one renderer read half a contract and half a trapdoor: an author who
inferred `properties.limit: 20` from the `object`/`filter` spelling got the
renderer's default 50 with zero diagnostics, because the key was stripped
before anything could read it. ADR-0078, on the element #5775 had just been
written to fix.
Maintainer ruling 2026-08-08, direction A — the #5611 rule applied again: the
delivered, authorized shape is the contract. Both keys take the shape
`ElementDataSourceSchema` already declares for its own `sort` / `limit`
(`SortItemSchema[]`, positive integer), imported from the shared source rather
than re-spelled, so the shorthand cannot drift into a third dialect.
`dataSource.*` still wins when both are written — the declaration documents the
precedence, it does not change it. The renderer's `?? 50` stays a renderer
fallback and is deliberately NOT a schema default: `.default(50)` would
materialize a limit on every parsed picker and turn an unset key into an
authored one.
Direction B (retire the flat family, make `dataSource` the single door) is not
dropped — it is a cross-element decision tracked as #6590 for v18, and this
change does not block it.
- packages/spec/src/ui/component.zod.ts — the two declarations + the rule the
next divergence sweep should follow (enumerate by the renderer's read
pattern, not by the key list a previous ruling quoted)
- packages/spec/src/ui/component.test.ts — retention, value rejection, and
shape parity with `ElementDataSourceSchema`
- packages/lint/src/validate-component-props.test.ts — the #5068 gate half:
the keys stop being unknown-key findings, and a wrong `limit` is now judged
instead of stripped
- generated: authorable-surface/ui.json, content/docs/references/ui/component.mdx
Refs #6276, #5775, #5068, #6590, ADR-0078
…/main
main's inline-shape depth budget changed how nested shapes render in the
generated reference, so my pre-merge `gen:docs` output and main's generator
merged clean but were jointly wrong (`PageTabsProps.items[].visibleWhen`
expanded on my side, collapsed on main's). Regenerated against the merged
generator — the delta vs main is now exactly the two `element:record_picker`
rows this PR adds.
@vercel

vercelBot commented Aug 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 8, 2026 7:32am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

112 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/spec)
  • content/docs/automation/approvals.mdx(via @objectstack/spec)
  • content/docs/automation/connectors.mdx(via @objectstack/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via packages/spec)
  • content/docs/concepts/north-star.mdx(via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/cli.mdx(via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/spec)
  • content/docs/kernel/services.mdx(via @objectstack/spec)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/rls.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx(via @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v16.mdx(via @objectstack/spec)
  • content/docs/releases/v17.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)
  • content/docs/ui/actions.mdx(via @objectstack/spec)
  • content/docs/ui/apps.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)
  • content/docs/ui/translations.mdx(via @objectstack/spec)
  • content/docs/ui/views.mdx(via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests protocol:ui tooling labels Aug 8, 2026
@qq9340100
qq9340100 marked this pull request as ready for review August 8, 2026 07:48
@qq9340100
qq9340100 added this pull request to the merge queueAug 8, 2026
Merged via the queue into main with commit 78f0be8Aug 8, 2026
26 checks passed
@qq9340100
qq9340100 deleted the claude/issue-6276-record-picker-sort-limit branch August 8, 2026 08:04
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:uisize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

element:record_pickersort / limit 扁平简写:renderer 兑现、schema 未声明(#5775 实施中新测出,A 表之外的第 7 处)

2 participants

@qq9340100@claude