Ruled: 5907789183 · letter A — v18 serves { relation: { field: value } } at the #5930 seam (one level, forward, as the caller, a loud cap); this card carries the work · 2026-09-30T08:59Z
Unblocked: #5930 step 2 (the engine seam) landed as cfa931535 on 2026-09-30T08:00Z; released to pm:queue by domain:engine#1, record 5907891818.
Blocked-by: #20887
Filed by the triage seat (objectstack-wide, seat post #6015 , session_01AavokzJ5DndAwitDXvKy4U) at the maintainer's request 「开一张 v18 的决策卡」. ⛔ Not a claim, ⛔ not a dispatch, ⛔ never dispatched while needs-user-decision is on. Graded: enhancement · priority:p3 · domain:engine · area:api · target:v18.
一句话问题
作者想查「客户行业是科技的商机」,最自然的写法是把条件写在关联字段下面。平台的类型接受这个写法,但数据查询会报 400;而同样的写法放到分析看板里却能查出结果。v18 要真的支持它 ,还是把这个写法从协议里去掉 ?
背景
Governing text
spec 声明 :packages/spec/src/data/filter.zod.ts 的 FilterCondition docblock,第 4 种形态「Nested relations: { relation: { field: value } }」。它写明类型和 schema 接受、查询引擎拒绝 。
re-check:git grep -n "4. Nested relations" origin/main -- packages/spec/src/data/filter.zod.ts → 1
裁决基准 (pm-dispatch 技能的「基本裁决原则」):spec 声明 > 实现 > 文档面;声明了却不兑现是实现缺口,只能补实现或退役 。所以「一直维持现状」不是选项,只是等裁决期间的过渡状态。
seam 架构 (ADR-0053 修订第 5 条,即 [finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 裁决的 D4 (b)):驱动收到的是已经在 seam 下译好的过滤条件 ,驱动本身不改。
协议声明、是否改协议
A 改的是 docblock 的描述:从「引擎拒绝」改成「引擎服务」。类型不收窄,属于扩大,Clause-②: no。
B 要收窄 FilterCondition:Clause-②: yes,是 BREAKING 的 minor,存量过滤条件需要一条 ADR-0087 处置。
前提(每条带 re-check,均已在 origin/main 96e724475c 跑过)
CRUD 数据口今天拒绝这个写法 ,点号路径('owner.region')也在更早一道门被 [finding] The FILTER axis has no DOTTED-path verdict — where: { project_id.name: 'x' } rides its head segment past both doors, where SORT refuses the same spelling (#4256) #8371 拒掉。
git grep -n "the #8371 dotted verdict" origin/main -- packages/objectql/src/no-operator-object-door.ts → 1
分析看板那一面今天是支持的 :cube 过滤把它拍平成点号成员(profile.verified),靠 cube 声明的 join 解析。
git grep -n "Nested relation (e.g." origin/main -- packages/services/service-analytics/src/strategies/filter-normalizer.ts → 1
分析的读权限范围那一面拒绝它 。
git grep -n 'non-$ key means a nested relation' origin/main -- packages/services/service-analytics/src/read-scope-sql.ts → 1
引擎里已有同形的机制 :expand 批量加载关联记录,用的就是对关联 id 的 $in 查询。
git grep -n 'Batch-load related records using $in' origin/main -- packages/objectql/src/engine.ts → 1
业务拉动(实测) :hotcrm 有 4 个 hook 手写同一段两步查询:先查成员行拿到线索 id,再对线索用 id: { $in: leadIds },而且第一步手挑了 top: 5000,超过 5000 条会静默截断 。
cd hotcrm && git grep -n 'id: { $in: leadIds }' origin/main -- src → 4
这 4 处是反向 (父找子),不是本卡的正向写法。hotcrm 的 271 行 filter / where 中,按正则扫描,正向嵌套写法为 0 。
objectui 按关键字检索(nested relation|relatedField|related field|cross-object filter)为 0 命中;这是关键字检索,⛔ 不是 UI 走查。
具体问题
v18 的平台契约里,「按关联记录的字段筛选」要么是服务的能力 (A),要么不存在 (B)。不裁时,今天的响亮拒绝继续有效,不影响 17.x 发版。
选项 × 真实代价
选项
做什么
客户可感知的后果
A 支持 (v18,排在 #5930 收敛的 seam 之后)
引擎在一个 seam 上把 { 关联: { 字段: 值 } } 下译成「以调用者身份查关联对象 → 对关联字段 $in 这些 id」,驱动不改(D4 (b))。首版只做一层 、只做正向 (子 → 父)。多值关联按「任一匹配」处理。
列表视图、API、AI 写的查询都能直接写「客户行业 = 科技」。CRUD 与看板对同一写法给同一个答案。多一次内部查询;关联集合很大时按上限响亮拒绝 ,⛔ 不截断。
B 退役 (v18 收窄类型)
FilterCondition 不再接受这个写法,在发布、保存时就报错,只剩「两步 + $in」一条路。分析看板的嵌套写法同时退役,点号成员写法保留。
作者和 AI 在保存时就被拦下,不用等到运行时。但这个能力要靠手写两步查询,像 hotcrm 那 4 个 hook 一样,会重复写、会截断。
业务含义直译
A = 像 Salesforce 那样,列表筛选可以直接写「客户.行业 = 科技」。平台替你做两步查询,并且按你的权限做。
B = 平台明说「不能跨对象筛选」,想做就在代码里自己先查一遍。像早期只支持单表筛选的轻量表格工具。
四轴论证(从业务立场)
os-decision-facets
Prior rulings read: nested relation / relation filter in docs/adr → 0 hits (only content/docs/references/api/contract.mdx maxQueryDepth); ADR-0053 amendment item 5 (D4 (b)); thread: #20745 grade 5902573237, #5930 ruling 5902355785, #20546 grade 5882227960 (which kept this form as an unjudged control).
推荐
推荐 A :v18 支持,排在 #5930 收敛落地 seam 之后;首版一层、正向、以调用者读权限执行、超上限响亮拒绝。
裁后执行(维护者只裁方向)
相关单与 PR
#20745 (已关,PR #20781 )· #20546 / PR #20744 · #5930 (v18 过滤器收敛,裁决 5902355785)· #20782 (skill 教 $in)· #8371 (点号路径拒绝)· ADR-0053 修订第 5 条 · ADR-0049 · ADR-0087
Ruled: 5907789183 · letter A — v18 serves
{ relation: { field: value } }at the #5930 seam (one level, forward, as the caller, a loud cap); this card carries the work · 2026-09-30T08:59ZUnblocked: #5930 step 2 (the engine seam) landed as
cfa931535on 2026-09-30T08:00Z; released topm:queuebydomain:engine#1, record 5907891818.Blocked-by: #20887
Filed by the triage seat (objectstack-wide, seat post #6015,
session_01AavokzJ5DndAwitDXvKy4U) at the maintainer's request 「开一张 v18 的决策卡」. ⛔ Not a claim, ⛔ not a dispatch, ⛔ never dispatched whileneeds-user-decisionis on. Graded:enhancement·priority:p3·domain:engine·area:api·target:v18.一句话问题
作者想查「客户行业是科技的商机」,最自然的写法是把条件写在关联字段下面。平台的类型接受这个写法,但数据查询会报 400;而同样的写法放到分析看板里却能查出结果。v18 要真的支持它,还是把这个写法从协议里去掉?
背景
{ account: { industry: 'tech' } }这类「关联字段下挂无运算符对象」的写法,内存驱动静默返回 0 行,SQL 驱动返回 400。4b4ee88fb)先把今天的答案统一成响亮拒绝:所有驱动都返回INVALID_FILTER/ 400,报错里指明可行的路(先查关联对象,再对关联字段用$in匹配它返回的 id)。文档里教这个写法的示例也删了。5902573237;[finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 上的指针5902585544)。[finding] a no-operator object under a lookup, master_detail or json field answers per driver: the declared nested-relation filter returns no rows on memory and a 400 on SQL, and a json object comparand deep-equals on memory and is refused on SQL #20745 已关,这个问题没有自然的挂靠处,所以单开此卡。Governing text
packages/spec/src/data/filter.zod.ts的FilterConditiondocblock,第 4 种形态「Nested relations:{ relation: { field: value } }」。它写明类型和 schema 接受、查询引擎拒绝。re-check:
git grep -n "4. Nested relations" origin/main -- packages/spec/src/data/filter.zod.ts→ 1协议声明、是否改协议
Clause-②: no。FilterCondition:Clause-②: yes,是 BREAKING 的minor,存量过滤条件需要一条 ADR-0087 处置。前提(每条带 re-check,均已在
origin/main96e724475c跑过)'owner.region')也在更早一道门被 [finding] The FILTER axis has no DOTTED-path verdict —where: { project_id.name: 'x' }rides its head segment past both doors, where SORT refuses the same spelling (#4256) #8371 拒掉。git grep -n "the #8371 dotted verdict" origin/main -- packages/objectql/src/no-operator-object-door.ts→ 1profile.verified),靠 cube 声明的 join 解析。git grep -n "Nested relation (e.g." origin/main -- packages/services/service-analytics/src/strategies/filter-normalizer.ts→ 1git grep -n 'non-$ key means a nested relation' origin/main -- packages/services/service-analytics/src/read-scope-sql.ts→ 1expand批量加载关联记录,用的就是对关联 id 的$in查询。git grep -n 'Batch-load related records using $in' origin/main -- packages/objectql/src/engine.ts→ 1id: { $in: leadIds },而且第一步手挑了top: 5000,超过 5000 条会静默截断。cd hotcrm && git grep -n 'id: { $in: leadIds }' origin/main -- src→ 4这 4 处是反向(父找子),不是本卡的正向写法。hotcrm 的 271 行
filter/where中,按正则扫描,正向嵌套写法为 0。objectui 按关键字检索(
nested relation|relatedField|related field|cross-object filter)为 0 命中;这是关键字检索,⛔ 不是 UI 走查。具体问题
v18 的平台契约里,「按关联记录的字段筛选」要么是服务的能力(A),要么不存在(B)。不裁时,今天的响亮拒绝继续有效,不影响 17.x 发版。
选项 × 真实代价
{ 关联: { 字段: 值 } }下译成「以调用者身份查关联对象 → 对关联字段$in这些 id」,驱动不改(D4 (b))。首版只做一层、只做正向(子 → 父)。多值关联按「任一匹配」处理。FilterCondition不再接受这个写法,在发布、保存时就报错,只剩「两步 +$in」一条路。分析看板的嵌套写法同时退役,点号成员写法保留。业务含义直译
四轴论证(从业务立场)
Account.Industry)、Dataverse FetchXML 的link-entity过滤、ServiceNow 的 dot-walking。A 把它落在一个 seam 上,和 [finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 的收敛方向(一处下译,驱动不改)一致,并让 CRUD 与看板对齐。B 让契约更小,但这个能力大概率会以新拼写回来,届时要再迁一次。os-decision-facets
Prior rulings read:
nested relation/relation filterindocs/adr→ 0 hits (onlycontent/docs/references/api/contract.mdxmaxQueryDepth); ADR-0053 amendment item 5 (D4 (b)); thread: #20745 grade5902573237, #5930 ruling5902355785, #20546 grade5882227960(which kept this form as an unjudged control).推荐
推荐 A:v18 支持,排在 #5930 收敛落地 seam 之后;首版一层、正向、以调用者读权限执行、超上限响亮拒绝。
$in」的实际开销没测过;objectui 没做 UI 走查。裁后执行(维护者只裁方向)
domain:engine·target:v18卡,Blocked-by:[finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 落地 seam 的那一步。卡上写明:data-engine.mdx示例,skills/objectstack-query([finding]skills/objectstack-queryteaches the nested relation filter{ relation: { field: value } }as a working form; no data-path driver serves it, and PR #20781 makes the engine refuse it #20782)同步;d1、d3,外加一条权限 pin 和一条上限 pin。domain:spec卡:收窄FilterCondition,Clause-②: yes,BREAKINGminor;存量过滤条件按 ADR-0087 处置,带指引拒绝;分析看板的嵌套写法同时退役,点号成员写法保留;[finding]skills/objectstack-queryteaches the nested relation filter{ relation: { field: value } }as a working form; no data-path driver serves it, and PR #20781 makes the engine refuse it #20782 的 skill 文案改成「这是唯一的路」。相关单与 PR
#20745(已关,PR #20781)· #20546 / PR #20744 · #5930(v18 过滤器收敛,裁决
5902355785)· #20782(skill 教$in)· #8371(点号路径拒绝)· ADR-0053 修订第 5 条 · ADR-0049 · ADR-0087