Skip to content

flow 的 delete_record / update_record 无法表达批量意图 —— 节点 schema 无键、执行器不传 options.multi,谓词批量写对所有 flow 平台级不可达,而节点描述符宣称支持 #5393

Description

@baozhoutao

Part of #5225(showcase 清扫流从未删过记录 —— 该单的根因,按跨车道协议转 spec 车道;发现与全部实测证据来自其 os-dev 的 needs_decision 报告,PM 转录)。

事实(真实 boot + built spec 的 safeParse 实测,origin/main @ ccba1bb)

  • DeleteRecordConfigSchema(packages/spec/src/automation/builtin-node-config.zod.ts:242)是 strictObject,只声明 objectName 与 filter 两键;multi / options.multi / bulk / all 逐一实测被 unrecognized_keys 拒绝。
  • CRUD 执行器(service-automation)调用引擎时从不传 options.multi。引擎契约(packages/objectql/src/engine-delete-dispatch.ts):标量 where.id → 按 id;否则 options.multi 为真 → deleteMany;否则抛 Delete requires an ID or options.multi=true。
  • 于是谓词批量删除从任何 app 的任何 flow 都不可达,而节点描述符写着 name: 'Delete Records' / description: 'Delete records matching a filter.';writtenRowCount 的文档与 Flow node filters silently blank date macros: the template engine consumes {…} before the query engine sees it #3810 擦除防护的前提都以「谓词 delete_record 会到达 deleteMany」为设计意图 —— declared ≠ enforced。
  • update_record 同病(engine.ts:5403 同款抛错,UpdateRecordConfigSchema 同样无批量键);showcase 三处 update_record 全用 filter: { id: … } 所以被掩盖。
  • 单元测试全程绿的原因已另记:service-automation 的 run-summary.test.ts:405 内联 fake 接受真引擎拒绝的谓词删除,且不在 engine-double 基线内(已评论至 os-dev 派发词/定义可加一行:测试假引擎的 delete() 必须路由 assertEngineDeleteDispatch —— 同一门禁一日两红(#5173、#5192) #5197,检测器盲点)。

PM 裁定(两轴分析由 #5225 的 dev 给出,PM 附议;留否决窗口 —— 维护者可否决,不等批准)

取 A:在 spec 给两个节点 config 声明批量意图键(一次命名,两节点共用),CRUD 执行器在作者声明后传 options.multi;B 与 C 否决。

完成判据

关联:#5225(现场与验收锚)、#5197(检测器盲点)、#5383(loop lint 盲区)、#3810、PD #5/#10/#12。

Activity

  1. self-assigned this
    on Aug 5, 2026
  2. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    认领:spec 车道第 1 轮
    会话:session_018fxLGQdatPbBUvCgiVxg6D
    分支:claude/issue-5393-flow-bulk-intent
    Worktree:objectstack-issue-5393
    域:domain:spec
    文件面:packages/spec/src/automation/builtin-node-config.zod.ts(+生成物)、packages/services/service-automation/**(执行器接线 options.multi + 真实契约测试)。⚠️ 跨入 domain:services 文件面,按跨域单一认领协议整单声明 —— engine/services 车道如有异议请在此回执;引擎拒绝路径保持现状(拒绝即契约),packages/objectql 零改动。

    按 issue 在册裁定 A 执行(维护者 2026-08-05 已核准本单进 P0 批,否决窗口视为已过)。#5225 挂 Blocked-by 本单,落地后由其收尾。


    Generated by Claude Code

  3. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    验收(spec 车道 PM,session_018fxLGQdatPbBUvCgiVxg6D):ACCEPT → PR #5485(draft;短程 12 checks 已绿,长程在跑,全绿后转 ready 入队)。

    裁定 A 落地完整,四条完成判据全部满足:

    1. multi(boolean,默认 false)在两个 config schema 同名同语义声明,拼写处方(bulk/all/multiple + options.multi 的错层答案)双门齐备,descriptor configSchema 同步 → Studio 可作者化;
    2. 执行器全路径转发 multi: cfg.multi === true,packages/objectql 零改动(拒绝即契约保持);
    3. os-dev 派发词/定义可加一行:测试假引擎的 delete() 必须路由 assertEngineDeleteDispatch —— 同一门禁一日两红(#5173、#5192) #5197 盲点在本文件关闭:delete 测试替身绑定生产者自己的 assertEngineDeleteDispatch,门禁判 pinned —— 不是又一份手抄判定;update 侧结构性做不到的原因如实立单(objectql 的 UPDATE dispatch 没有共享判定函数 —— delete 有 resolveEngineDeleteDispatch,update 的同款三分支只是 engine.ts 里的一个内联 throw #5480);
    4. 反向验证 7/10 红且方向先判,三条不红的理由逐条诚实交代,其中一条被识别为「断言太弱以致挺过它要钉的回退」并当场加强 —— 这是反向验证该有的用法。

    消费半径按规则的调用方扫(cli/lint/metadata-protocol/objectql 全绿),生成物全套整体重生成零手改。两张范围外单已核实并代分诊:#5480(engine 队列:update dispatch 判定抽取)、#5482(devx 队列:multi:true 空 filter 的 authoring 告警,定级方案 1,待本 PR 落地后派)。

    #5225(showcase 清扫流)在本 PR MERGED 后解锁收尾,届时我在该单留解锁评论。


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions