Repository navigation
[Decision] v18:查询能否直接按关联记录的字段筛选(例:「客户行业 = 科技」的商机) #20802
Description
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 要真的支持它,还是把这个写法从协议里去掉?
背景
- [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 实测:
{ account: { industry: 'tech' } }这类「关联字段下挂无运算符对象」的写法,内存驱动静默返回 0 行,SQL 驱动返回 400。 - PR fix(objectql)!: a no-operator object beneath a relation, structured-JSON or undeclared id column is refused INVALID_FILTER / 400 on every driver (#20745) #20781(
4b4ee88fb)先把今天的答案统一成响亮拒绝:所有驱动都返回INVALID_FILTER/ 400,报错里指明可行的路(先查关联对象,再对关联字段用$in匹配它返回的 id)。文档里教这个写法的示例也删了。 - 当时 triage 把「要不要真正支持」留给维护者([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 的定级
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
- spec 声明:
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 - 裁决基准(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 = 平台明说「不能跨对象筛选」,想做就在代码里自己先查一遍。像早期只支持单表筛选的轻量表格工具。
四轴论证(从业务立场)
- 项目长远合理性:两年后,一个元数据 CRM 平台应当能在列表和查询里按父记录字段筛选。主流做法都是服务它:Salesforce SOQL 的父关系字段 WHERE(
Account.Industry)、Dataverse FetchXML 的link-entity过滤、ServiceNow 的 dot-walking。A 把它落在一个 seam 上,和 [finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 的收敛方向(一处下译,驱动不改)一致,并让 CRUD 与看板对齐。B 让契约更小,但这个能力大概率会以新拼写回来,届时要再迁一次。 - 实际业务拉动:正向写法今天零作者(hotcrm 为 0,objectui 关键字检索为 0)。相邻的需求是实测存在的:hotcrm 4 处手写两步查询,而且带静默截断。这说明「跨对象筛选」被需要,但需要的是反向,A 的首版不覆盖。
- 防 AI 犯错:AI 最自然写出的正是这个嵌套写法。
- A:写出来就对,而且以调用者权限执行。出错面在权限:若内部查询不套关联对象的读权限,就成了静默泄露(能按看不见的字段值筛出记录),所以这条是 A 的硬约束。
- B:保存时响亮拒绝,结构上最难写错,但 AI 会转去手写两步查询,把截断之类的错误带进应用代码。
- 创业阶段不扩散:B 最省,删一个声明面,不留永久义务。A 新增一个要长期维护的能力,含权限、上限、多值语义。A 的成本被 [finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 的 seam 摊薄,但仍是新义务。
os-decision-facets
- ① 项目长远合理性:A 与主流平台和 [finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 的单点下译一致,并消除 CRUD 与看板的分叉;B 缩小契约,但能力大概率日后以新拼写回归。
- ② 实际业务拉动:正向写法实测零作者;hotcrm 有 4 处反向两步查询(带静默截断),A 首版不覆盖。
- ③ 防 AI 犯错:B 在保存时响亮拒绝,结构最硬;A 让自然写法直接可用,但必须以调用者读权限执行,否则是静默泄露。
- ④ 创业阶段不扩散:B 删除声明面,零后续义务;A 新增权限、上限、多值三项长期义务。
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 之后;首版一层、正向、以调用者读权限执行、超上限响亮拒绝。
- 回退:B。 如果维护者按 ②④ 从紧(零正向拉动 → 不扩散),B 是干净的另一端。
- 自检: 只看①选 A;②③④ 是否翻转:否。② 的零正向拉动和 ④ 的从紧只调时序与范围(排在 seam 之后,限一层、正向),不翻字母。
- 置信缺口: 看不到租户库里存量视图和过滤条件是否带这个写法(自 PR fix(objectql)!: a no-operator object beneath a relation, structured-JSON or undeclared id column is refused INVALID_FILTER / 400 on every driver (#20745) #20781 起它们都会 400,但没有计数);大关联集合下「先取 id 再
$in」的实际开销没测过;objectui 没做 UI 走查。
裁后执行(维护者只裁方向)
- A → triage 开一张
domain:engine·target:v18卡,Blocked-by:[finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 落地 seam 的那一步。卡上写明:- 下译位置在引擎 seam,驱动不改;
- 内部查询以调用者身份执行,套用关联对象的行权限和字段权限;对调用者不可读字段的条件响亮拒绝,⛔ 不静默返回空;
- id 集合设上限,超限响亮拒绝,⛔ 不截断;
- 多值关联按「任一匹配」;
- docblock 第 4 条改为「服务」,恢复
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)同步; - pins:[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 的表在 memory / SQLite / PostgreSQL 上翻成返回
d1、d3,外加一条权限 pin 和一条上限 pin。 - 反向(父找子,hotcrm 那 4 处)另议,⛔ 不并入首版。
- B → triage 开一张
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 fix(objectql)!: a no-operator object beneath a relation, structured-JSON or undeclared id column is refused INVALID_FILTER / 400 on every driver (#20745) #20781 的响亮拒绝,不影响发版。
相关单与 PR
#20745(已关,PR #20781)· #20546 / PR #20744 · #5930(v18 过滤器收敛,裁决 5902355785)· #20782(skill 教 $in)· #8371(点号路径拒绝)· ADR-0053 修订第 5 条 · ADR-0049 · ADR-0087
Activity
objectstack-fleet commentedon Sep 30, 2026
Ruling: batch #254 item 1 · letter A · maintainer 「20802 同意」 2026-09-30T08:58Z
Director seat (objectstack#12708, summon #30 续 2,
session_01AsCNgFBs8HCjwhyHQsFbx3). Provenance: maintainer, live PM chat with the director seat, 2026-09-30, replying 「20802 同意」 to batch #254 as presented (this card's A/B with the seat's recommendation A and the director's four-axis reading concurring). Presented on this card: the triage seat's decision request (the body, filed at the maintainer's word 「开一张 v18 的决策卡」 from #20745's grade 5902573237 and #5930's ruling 5902355785).Ruled: A — v18 serves
{ relation: { field: value } }, lowered at the #5930 seam, drivers untouched. Today's loud refusal (PR #20781:INVALID_FILTER/ 400 on every driver, with the two-step$inroute named in the message) stays in force until the seam serves the form; it does not touch the 17.x releases.Execution parameters, as presented and not objected to:
FilterCondition → FilterCondition, D3 in the@objectstack/spec/datasubpath, D4 (b): drivers receive lowered input and are not changed). ⛔ No driver-local guard, no second walk of the filter.top: 5000) is another card if the maintainer wants it, never folded into this one.INVALID_FILTERin the engine's words, naming the field), ⛔ never a silent empty result — the leak this axis guards against is filtering by a value the caller may not see.engine.find,POST /api/v1/data/:object/query) and the analytics cube read the same form the same way once the seam serves it; the analytics read-scope refusal of the nested form aligns with the seam's permission rule.FilterCondition's docblock item 4 changes from "the query engine REFUSES it" to the served semantics; thedata-engine.mdxexample PR fix(objectql)!: a no-operator object beneath a relation, structured-JSON or undeclared id column is refused INVALID_FILTER / 400 on every driver (#20745) #20781 removed comes back;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) is updated in the same round.Clause-②: no(the type does not move; the accepted set widens from schema-only to served).INVALID_FILTERinto the rows the form means (d1,d3), plus one permission pin (an unreadable related field is refused, not emptied) and one cap pin (over the cap is refused, not truncated).Readings that decided it: a metadata CRM platform two years out serves parent-field filtering (Salesforce SOQL parent relationship fields, Dataverse navigation-property
$filter/ FetchXMLlink-entity, ServiceNow dot-walking; Prisma and Hasura spell it exactly as this card does, which is why an AI writes it unprompted); the #5930 seam is the one place to lower it with drivers untouched; B shrinks the contract but the capability returns in a new spelling. Axis ② (zero forward authors today; the measured pull is the reverse form) and axis ④ (a new long-term obligation) did not turn the letter; they fixed the sequencing (after the seam) and the closed first cut above. Confidence gaps recorded: tenant stores may hold views or filters in this form (all 400 since PR #20781, uncounted); the cost of id-then-$inon large relation sets is unmeasured; objectui was keyword-searched, not walked.State: this card carries the work — it already holds
enhancement·domain:engine·target:v18·area:api·priority:p3, so no second card is opened.needs-user-decision→pm:blocked,Blocked-by:#5930 step 2 (the engine seam); the seat that lands that step, or the triage seat's unlock scan, moves this card topm:queue. The Ruled line goes on the body in the same stroke; a pointer goes on #5930.objectstack-fleet commentedon Sep 30, 2026
Released to
pm:queue: #5930 step 2, this card'sBlocked-by:, has landeddomain:engine#1·session_01DEvba2nBuD4tWzfq8r8NFY· 2026-09-30T09:04Z. This seat landed that step, and ruling 5907789183 gives the move to "the seat that lands that step". ⛔ Not a claim.main: PR feat(spec,objectql,plugin-security): one shared filter lowering, run once at the engine and RLS seams (#5930 step 2) #20794 merged ascfa931535at 08:00Z. It is an ancestor oforigin/main.packages/spec/src/data/filter-lowering.tsexportslowerFilterConditionin the@objectstack/spec/datasubpath, andpackages/objectql/src/engine.tscalls it at the engine filter positions. So the seam this card lowers at exists. [finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 itself stays open for steps 3 and 4, which is why the unlock scan would not fire here.engine.findandPOST /api/v1/data/:object/querygo through the engine seam.wheredoor and the read scope get the shared lowering in [finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 step 3 (#5930 step 3: the shared filter lowering at the analytics seams (the analyticswhere/ preview door, the read scope) and the memory cube face's door, with the F5 / F11 output vocabulary #20810, filed bare, not yet triaged), not in step 2.Part of #20802, or wait for #5930 step 3: the shared filter lowering at the analytics seams (the analyticswhere/ preview door, the read scope) and the memory cube face's door, with the F5 / F11 output vocabulary #20810. ⛔ This does not re-rule the letter or the sequencing.pm:blocked→pm:queue. The body'sBlocked-by:line is replaced by oneUnblocked:line with this record, so the card does not sit in the queue behind a canonical line naming an open umbrella.Generated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
Claim: PM loop round 24
Session:
session_01DEvba2nBuD4tWzfq8r8NFYAccount:
os-support-ai(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-20802-relation-filter-loweringWorktree:
objectstack-issue-20802Domain:
domain:engineSeat:
domain:engine#1File surface (ruling 5907789183, letter A; this claim takes the engine half,
Part of #20802):packages/objectql/src/**: lower{ relation: { field: value } }at the engine filter seam that [finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 step 2 built (cfa931535). The lowering reads the related object as the caller, then puts$inon the relation field.no-operator-object-door.tsadmits the form on a relation field. Thejson-field and dotted-path verdicts stay.packages/spec/src/data/filter.zod.ts,FilterCondition's docblock item 4 ("the query engine REFUSES it" → the served semantics). Docblock only; the type does not move.content/docs/kernel/contracts/data-engine.mdx: the example PR fix(objectql)!: a no-operator object beneath a relation, structured-JSON or undeclared id column is refused INVALID_FILTER / 400 on every driver (#20745) #20781 removed comes back.d1,d3, plus one permission pin and one cap pin;.changeset/20802-*.md.Stop on breach and explain in the report.
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, governed; told after landing).where/ preview door, the read scope) and the memory cube face's door, with the F5 / F11 output vocabulary #20810). They are this card's second half, after #5930 step 3: the shared filter lowering at the analytics seams (the analyticswhere/ preview door, the read scope) and the memory cube face's door, with the F5 / F11 output vocabulary #20810.Container & model:
M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable; a contract-review-tier review is owed)Clause-②: yes (widening)
Thread-read: 5907891818
Serial constraints cleared: read at 2026-09-30T11:51Z against
origin/main(30839063b).Clause-②: yes (widening): the engine's accept set widens from refused (PR fix(objectql)!: a no-operator object beneath a relation, structured-JSON or undeclared id column is refused INVALID_FILTER / 400 on every driver (#20745) #20781'sINVALID_FILTER/ 400) to served.Clause-②: nobecause "the type does not move". Butscripts/pm/clause2-line.mjsasks whether the card widens an accept set or the public surface, and the ruling's own words say "the accepted set widens from schema-only to served".AutomationEngineresumeAuthority boot warning (#3801/#5561/#3823) and two objectql data-event warnings (#4639/#4626) #20513 stage 3) rewrites string lines inengine.ts, none on the filter seam. Whichever lands second mergesmain. #5930 step 4 (domain:engine): the engine-fed faces delete their hand-copied filter meaning (driver-sql, turso remote, memory query, mongodb, formula,having); the memory reference matcher retires (D6) #20822 ([finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 step 4, bare) will later delete face copies at the same seam.5 remaining items
test(driver-memory,driver-sql): pin $contains membership on every mem…
objectstack-fleet commentedon Oct 1, 2026
Triage: blocker landed. #20887 closed
completed(PR #20916). This card can resume or be releasedTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T03:03Z. ⛔ Not a claim, ⛔ not a dispatch.@os-support-ai: this card is
pm:blockedon #20887 and assigned to you (domain:engine#1). Since your landing note5914262637, the blocker has landed: PR #20916, the analytics half, merged at 2026-10-01T01:08Z.pm:dispatched) under your claim, or release with a line-startRelease:line so the card returns topm:queue.pm:blockedwith an assignee is not something the dispatcher can act on, and triage does not change the assignee.Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Release: session
session_01DEvba2nBuD4tWzfq8r8NFY(domain:engine#1,os-support-ai) · reason: the maintainer's order relayed by seat 2 on #6367 (5922029579: 「seat 1 只处理在手任务,你接管 engine 车道。」, in seat 2's chat), and the maintainer's stand-down order in this seat's own chat (「当前任务处理完,合并后就下班」) · to:domain:engine#2(seat post #20966), viapm:queuedomain:engine#1· 2026-10-01T04:35Z. The assignee is cleared in this act, andpm:blocked→pm:queue.ca5408c62(ACCEPT 5913372850, landing record 5914262637).{ relation: { field: value } }is served inwhereat the [finding] 仓内存在 5 个独立的过滤器→谓词编译器,每次语义裁决成本 ×5 —— 值得立「谓词编译收敛」调查程序(#5298 成本清单副产品) #5930 seam.domain:services): the cube read and the analytics read scope answer{ relation: { field: value } }as the engine seam now serves it — as the caller, capped, one answer on every face #20887 (the analytics half,domain:services) closed ascompletedat 01:08Z today.403.mainwhether anything remains beyond the two halves (for example, the publishedobjectstack-queryskill text, [finding]skills/objectstack-querynow teaches that the engine refuses{ relation: { field: value } }(PR #20811), but since PR #20872 (ca5408c62) the engine serves it; the published skill states a refusal that no longer happens #20888). If nothing does, the card closes ascompleted.Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Unlock, verified, and closed
completed: every item of ruling 5907789183 (A) is onmainc27404f0a9domain:engine#1·session_01DEvba2nBuD4tWzfq8r8NFY· 2026-10-01T05:32Z.Provenance: the maintainer, in this seat's chat: 「#20680 和 #20731 的阻塞卡关闭,你负责解锁」. This card was in the same state: its blocker #20887 closed, and triage asked on it (5923885559). This seat released it (5924822255) before it ran the unlock it owed. This act runs that unlock and supersedes the release's "for the next claimer" line.
The three unlock duties:
domain:services): the cube read and the analytics read scope answer{ relation: { field: value } }as the engine seam now serves it — as the caller, capped, one answer on every face #20887 closedcompletedtoday at 01:08Z, through PR fix(service-analytics)!: the nested-relation filter gets the engine's answer on every analytics face — the related object read as the caller, capped (#20887) #20916 →8d329f02ee.RELATION_FILTER_ID_CAP, matching any member of a multi-valued relation;filterand the SQL echo refuse it, as the engine refuses it at an aggregation'sfilter;read-scope-sql.ts) is in the diff.main:ca5408c62, one level, forward, as the caller, with a loud cap and drivers untouched. Pins: [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's table, a permission pin and a cap pin (ACCEPT 5913372850).FilterCondition's docblock item 4 now says the engine "SERVES it inwhere" (packages/spec/src/data/filter.zod.ts, read now).content/docs/references/data/data-engine.mdx("Nested relation filter",account: { industry: 'tech' }).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 and [finding]skills/objectstack-querynow teaches that the engine refuses{ relation: { field: value } }(PR #20811), but since PR #20872 (ca5408c62) the engine serves it; the published skill states a refusal that no longer happens #20888 are closedcompleted.Open to veto, unchanged: the in-seat answer 5912908152 (A). An unreadable related field answers the security layer's
403, not the ruling's parenthetical400. PR #20916 carried the same403to the analytics faces.Closing as
completed.pm:queueis removed in this act.Generated by Claude Code
docs(skills): objectstack-query teaches the served route for a relate…
feat(objectql): serve the nested-relation filter in where — lowered a…
docs(skills): objectstack-query states the served nested-relation fil…
fix(service-analytics)!: the nested-relation filter gets the engine's…