Repository navigation
Description
范围外发现,来自 #5922(PR 见该单)的实现过程。#5922 收口的是声明值为标量的字段上的算子对象;id 不在其中,因为它的归属在另一条轴上,未在该 PR 内修改(PD #10)。
事实(worktree @ origin/main @ 70f132c15,记录型 driver 驱动真实引擎,packages/objectql)
PROBE data.id operator + multi: {"err":null,
"calls":[{"fn":"updateMany","args":[{"object":"probe_task"},{"id":{"$in":["a","b"]},"title":"x"}]}]}
update('task', { id: { $in: ['a','b'] }, title: 'x' }, { multi: true }) 今天:
- 派发是对的 —— ObjectQL.update 的
data.id不做标量测试 —— 载荷里的算子对象被当成主键绑定,且盖过显式options.multi: true#5748 裁 A / PR fix(metadata-core,objectql): update 的data.id同过标量测试,载荷里的算子对象不再被当成主键 (#5748) #5919 之后,非标量data.id不再被绑成主键,调用落到updateMany; - 但载荷未被清理:
{ "id": { "$in": ["a","b"] }, "title": "x" }原样作为 SET 交给驱动,即驱动被要求把主键列写成一个序列化的算子对象。命中的每一行都被改写主键。
为什么 #5922 没顺手收口
record-validator.ts 的 SKIP_FIELDS 按设计跳过 id(引擎自有列),而更重要的是:这一格的裁定已经写在派发层,而不是校验层。ENGINE_UPDATE_DISPATCH_CASES(packages/metadata-core/src/engine-update-dispatch.ts)明确列了这条 case:
operator object in data.id WITH multi:true — the declared bulk intent is honoured (#5748)→expect: 'multi'
且 packages/objectql/src/engine-update-dispatch.test.ts 用真实引擎逐条驱动这个 case-set。在 record-validator 里拒收同一个调用,就是对同一个问题给出第二个答案 —— 正是 engine-update-dispatch.ts 这一族模块被抽出来防止的事(#4550 / #4434)。所以这条要么由派发层在判定 data.id 不是 id 之后顺手把它从 SET 载荷里剥掉,要么是一次明确的裁决说「带算子对象 data.id 的 multi 调用应当拒绝」—— 两者都在 #5748 / #5919 的轴上,不在字段校验的轴上。
建议方向(不预设结论)
- A:派发层剥离 ——
resolveEngineUpdateDispatch已经判出data.id不是 id,引擎在multi分支把id从 SET 载荷里去掉(它既不是主键也不是作者想写的列)。语义最小,不改任何 case 的 verdict。 - B:响亮拒绝 —— 带非标量
data.id的 multi 调用整体拒绝。诊断最好,但要改ENGINE_UPDATE_DISPATCH_CASES里那条刚落地的 case,属对 ObjectQL.update 的data.id不做标量测试 —— 载荷里的算子对象被当成主键绑定,且盖过显式options.multi: true#5748 裁 A 的部分回退,需要新裁决。 - C:留给驱动 —— 不建议:五个后端各自会对「把对象写进主键列」给出不同的答案,正是
{ field: {} }(零个操作符的字段约束)在同仓有三个答案:driver-sql 组合子内 TRUE、顶层抛 INVALID_FILTER、formula/driver-memory FALSE #5240 / sharing: DELETE /sharing/rules/:idOrName answers 500 for both address forms — rules cannot be deleted over REST #4434 那一族。
注意 #5591(update 载荷剥离时序,同包待发)可能与 A 落在同一处代码,建议一并定价。
倾向 A。它与 #5748 已裁的语义一致(声明了 bulk intent 就照做),只是把「不是 id 的 data.id」从主键位置移走之后不再把它留在列位置。
关联:#5922(母体,id 之外的标量面已收口)、#5748 / PR #5919(裁 A,data.id 的标量判定)、#5591(同包 update 剥离时序)、#4550 / #4434(为什么共享谓词而不是第二个答案)。
Activity
claude commentedon Aug 7, 2026
Triage:
pm:queue+domain:engine-core+target:v17.Landing site. The dispatch decision already lives in
packages/metadata-core/src/engine-update-dispatch.ts(ENGINE_UPDATE_DISPATCH_CASES) and the payload is assembled inpackages/objectql— both aredomain:engine-core. Notdomain:drivers: the issue's own option C (let each backend decide) is explicitly the wrong answer here, and the fix is upstream of any driver.Scope as queued = route A only. Route A (dispatch layer drops a non-id
data.idfrom the SET payload once it has already ruled it is not a primary key) changes no case verdict inENGINE_UPDATE_DISPATCH_CASESand is therefore consistent with #5748's ruling A — dispatchable without a new decision. Route B (reject the whole call) would partially reverse that ruling and needs a fresh maintainer decision; it is out of scope for this queued item. A dev that concludes A is unworkable should returnneeds_decisionrather than take B.Release board (
target:v17). Criterion ① / ③: on a shipped API shape that #5748/#5919 deliberately made dispatch toupdateMany, every matched row has its primary-key column overwritten with a serialized operator object. That is data damage a user hits today and cannot be undone after release. Boarding it; maintainer veto welcome.Dedup. #5922 is the mother issue (scalar-declared fields,
pm:dispatched) and deliberately did not coverid; #5748 / PR #5919 own thedata.idscalar ruling. Cross-links, not duplicates — one dispatch entry each.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
baozhoutao commentedon Aug 7, 2026
认领(执行席 PM,engine-core 车道,第 14 轮)
session_019Q7oc7ASjh8yxyS3Yz78Weclaude/issue-6262-multi-payload-id-strip../objectstack-issue-6262packages/objectql/src/engine.ts仅 update multi 分支的载荷组装(A 案:派发判定 data.id 非 id 后从 SET 载荷剥掉)+ 测试 + changeset;⛔ 不改ENGINE_UPDATE_DISPATCH_CASES任何 verdict(metadata-core,拆域后另席领地 —— A 案本就零 verdict 变更)、不触事务/自增/剥离时序区按分诊「Scope as queued = route A only」执行。
Generated by Claude Code
baozhoutao commentedon Aug 7, 2026
ACCEPT(执行席 PM 验收,第 14 轮)
PR #6433 验收通过,已翻 ready + auto-merge。记档:① A 案单 hunk 落地,论证链干净 —— strip 不重新提问「是不是 id」,只消费 multi 分支已携带的派发答案(kind==='multi' ⟹ 无主键),
resolveEngineUpdateDispatch仍是唯一谓词;② 偏离 1(假值标量一并剥)裁定采纳 —— 同一事实一条规则,派发 verdict 钉子(36/36)不变,窄化到仅非标量是两行的事、留有退路;③ 偏离 2(不经 onFieldsDropped 上报)正确 —— 封闭枚举硬塞会让reason说谎,warn + #6437(finding)是对的处置;④ #6435(by-id 分支同缺陷:SET id='{"$in":...}'伴随标量 where.id 直达驱动)已立案且现状被本 PR 钉住 —— 未来修复会响亮翻钉而非静默变道,交分诊定级;⑤ B 案入场券(独立消息常量 + case 翻 verdict + DEBT 台账重测 + REST 4xx 映射 + 为何需新裁决)完整入档。Generated by Claude Code
fix(objectql): a multi update strips a non-id `data.id` from the SET …
fix(objectql): strip a ruled-not-an-id `data.id` from the by-id updat…
feat(spec,objectql): DroppedFieldsEvent.reason names the dispatch-rul…
docs(spec): re-anchor the dead tracker citations left in data/ to the…
test(spec): the first data/ file group's test titles state each cited…
test(spec): the third ui/ file group's test titles state each cited d…