Skip to content

object-grid 的 inputs 声明 filters(复数),渲染器只读 filter(单数):按已发布词汇写筛选条件静默无效 #7119

Description

@yinlianghui

发现于 objectstack#6953 的实施过程(objectui 分支 claude/issue-6953-datasource-block-wiring),不在该单范围内,单独立单。

事实(objectui origin/main @ 65bb513dc 实测)

object-grid(以及同一渲染器的 view:grid 别名)的注册 inputs 声明的是复数 filters:

packages/plugin-grid/src/index.tsx
ComponentRegistry.register('object-grid', ObjectGridRenderer, {
  inputs: [
    { name: 'objectName', ... },
    { name: 'columns', ... },
    { name: 'filters', type: 'array', label: 'Filters' },   // ← 复数
  ]
});

而渲染器读的是单数 filter:

packages/plugin-grid/src/ObjectGrid.tsx:440  const schemaFilter = schema.filter;
packages/plugin-grid/src/ObjectGrid.tsx:615  params.$filter = schemaFilter;

schema.filters 在 ObjectGrid.tsx 里 grep 不到任何读点(唯一命中是文件头注释里的一句 "Search, filters, pagination")。同一仓的 list-view 声明的是单数 filter,与它自己的读点一致 —— 两个同族 block 对外发布了两种拼写,只有一个是真的。

症状

作者(或 AI)按 object-grid 已发布的 inputs/manifest 写 filters: [...],查询里没有 $filter,拿到全表 —— 不报错,SDUI 保存门也不会报 unknown-prop(filters 是已声明键)。反过来,写真正生效的 filter 时,filters 才是被 manifest 承认的那个名字,于是「发布的词汇」和「运行时读的键」指向相反。objectstack#4413 的形状,叠加 objectui#3407 那种「声明与读点拼写不一致」的变体。

为什么现有门禁看不见

  • check:react-declaration-parity 比的是两侧声明(spec zod props ↔ 本仓 inputs),不观察渲染器。
  • apps/console/src/__tests__/public-block-binding-reach.test.tsx 只问一个问题:声明了 objectName 的 block 有没有把对象名送到数据层。object-grid 送到了,所以它在那里是绿的 —— 该测试的文档也明确写了它不是「每个声明的 input 都被消费」的检查。

建议范围(需要判一次方向,别直接猜)

两条路,选哪条会落进公开契约,建议由维护者定:

  • A(推荐):把 inputs 改成单数 filter,与 list-view、与 spec 词汇、与渲染器读点三方对齐;filters 作为从未生效过的错误拼写直接去掉(它没有任何读点,不存在「已在野的可用写法」需要兼容)。
  • B:让渲染器额外读 filters。⛔ 这会把一个错误拼写固化成第二套 de-facto 契约(AGENTS.md #0.1),不建议。

无论哪条,需要一条钉子:按 inputs 声明的名字写筛选条件时,$filter 必须出现在查询里。

Activity

  1. claude commented on Aug 9, 2026

    @claude
    Contributor

    Triage: needs-user-decision (routing repo:objectui kept as filed).

    • Why the decision box: the fix direction lands in a published contract surface — object-grid's registered inputs vocabulary. Option A removes the declared-but-never-read plural filters key; option B would canonize the misspelling as a second de-facto contract (the card itself argues B is barred by AGENTS.md #0.1). The filer explicitly requests a direction ruling; nothing in the thread decides it yet, so this is a pending decision, not a made one.
    • Premise check on objectui origin/main @ 53b7b88: inputs declares plural filters twice (packages/plugin-grid/src/index.tsx:62,:79); the renderer reads singular schema.filter (ObjectGrid.tsx:440) and has zero schema.filters reads (counter-checked). Defect stands as described.
    • Recommendation for the maintainer: A — filters has no read point on any ref, so there is no in-the-wild working usage to preserve; A aligns declaration with list-view, the spec vocabulary, and the renderer in one move. On approval this becomes a small queue card with the pin the card already sketches.
    • Dedup: no open shadow (objectui service-automation: update_record reports success when written fields are silently stripped — no observability for dropped writes (split from #3356) #3407 is the cited precedent class, closed face); clean.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. yinlianghui commented on Aug 9, 2026

    @yinlianghui
    CollaboratorAuthor

    PM 分诊(objectui 分片,session_01GTRjn8xBqp75dk7kFupVRt):挂 needs-user-decision 入决策箱 —— object-grid 声明 input filters(复数)而运行时读 filter(单数),按已发布词表授权的作者拿到静默无过滤查询。方向题与 objectui#3951(grid 列键 name/field 双拼写)完全同族:都是「已发布作者面 vs 运行时读点」的收敛方向选择,rename 破坏按词表授权的存量、teach-alias 是 AGENTS.md #0.1 禁止的容忍写法。实施 agent 推荐 rename(声明向实现收敛)。建议维护者与 objectui#3951 同席一并裁,两单一个原则定调(声明侧收敛 or 实现侧收敛),避免两次裁出相反方向。


    Generated by Claude Code

  3. yinlianghui commented on Aug 9, 2026

    @yinlianghui
    CollaboratorAuthor

    顺手记录一条本单实施时必须一并处理的相邻事实(发现于 objectstack#7118 / objectui#3895 的实施,objectui PR #3981;不另立单,因为它不是可独立完成的工作 —— 它是本单修法的一个约束)。

    事实(objectui origin/main @ 5bfaabde0 实测)

    object-grid 读到的 schema.filter 是逐字节原样送上 $filter 的,没有经过仓库的单一 filter 汇聚点:

    packages/plugin-grid/src/ObjectGrid.tsx:613  // Support new filter format
    packages/plugin-grid/src/ObjectGrid.tsx:614  if (schemaFilter && Array.isArray(schemaFilter)) {
    packages/plugin-grid/src/ObjectGrid.tsx:615    params.$filter = schemaFilter;
    

    (导出路径同源::1417 的 const filter = Array.isArray(schemaFilter) ? schemaFilter : undefined;)

    它是这条链上唯一不走 toFilterNode / mergeFilterNodes 的消费者 —— plugin-list 的 buildEffectiveFilter(ListView.tsx:243)、plugin-view 的 ObjectView(:432)、packages/react 的 ElementDataSourceGate(:210)、以及 PR #3981 刚接上的 RelatedList 都走。

    为什么这是本单的约束,而不是另一个 bug

    现在它不咬人,原因恰恰就是本单报告的缺陷:inputs 只声明复数 filters,单数 filter 无人声明,所以作者根本到不了这个读点(写 filter 会被 sdui-parser 判 unknown-prop)。今天到达 schema.filter 的只有 ElementDataSourceGate 合成出的 AST —— 已经是 AST,原样透传是对的。

    本单一旦给作者的筛选条件接上读点,作者写的就是 spec 词汇 ViewFilterRule[]([{ field, operator, value }]),而这个形状原样送上 wire 会被服务端拒绝:isFilterAST 对「对象数组」为 false,wire face 答 400 INVALID_FILTER —— objectui#3431 已在真实后端上实测过这条(showcase 的 showcase_task.in_progress view:原样发 400,降为 [['status','equals','in_progress']] 后 200 带回它的 2 行)。

    所以修法二选一都要带上降级:

    1. 若把复数 filters 接成读点 → 该读点必须过 toFilterNode(它同时把 defaultFilters 那类 MongoDB 风格对象一并收进来);
    2. 若改成把声明面重命名为单数 filter(与渲染器对齐)→ 同一处仍要过 toFilterNode,否则本单会把「声明了没人读」换成「声明了、读了、发出去被 400」—— 从静默错答变成必然失败,不是修好。

    判据:toFilterNode 的 doc 自己写明它是「离 wire 最后一跳」的唯一合法降级位置,所以不要在别处折叠(那会把 AST 三元组写进 spec 声明为 rule 数组的槽位,AGENTS.md #0.1 反向)。


    Generated by Claude Code


    Generated by Claude Code

  4. os-zhuang commented on Aug 10, 2026

    @os-zhuang
    Contributor

    Migrated to objectstack-ai/objectui#4041 under #7167 (file-at-destination ruling, maintainer 2026-08-10). Native GitHub issue transfer is not available to this session's credential, so the card was recreated at the destination; this thread stays as the authoritative history and is linked from the new card. Closing as not planned here — moved, not rejected.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions