Repository navigation
Closed
Labels
domain:devxDomain lane — seat card indexed by label:pm:seatDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loopReady for the PM dispatch loop
Description
发现于 #5393(给 flow update_record / delete_record 补 multi 批量意图键)的实现过程,PR 见该单。属 PD #10 的范围外发现,#5393 内刻意不修 —— 加这条守卫等于在该单里发明 issue 没要求的新语义,交 PM 定级。
事实
#5393 落地后:
config: { objectName: 'lead', multi: true }(没有filter)会走到data.delete(objectName, { where: {}, multi: true }),引擎按resolveEngineDeleteDispatch判为multi→driver.deleteMany({})→ 整表。- 这不是 bug,是声明:引擎自己的派发表就把
{ what: 'multi with no predicate at all', options: { multi: true }, expect: 'multi' }列为合法(packages/objectql/src/engine-delete-dispatch.ts),平台层面「显式的整表删除」一直是可表达的。 - Flow node filters silently blank date macros: the template engine consumes
{…}before the query engine sees it #3810 的抹除守卫不会拦它,而且是刻意的:packages/services/service-automation/src/builtin/crud-nodes.ts的注释写明「deliberately keyed on 'a condition the author wrote is gone', not on 'the filter is empty'」,crud-filter-guard.test.ts还有一条用例专门钉住「an intentionally empty filter is still allowed (nothing was erased)」。
在 #5393 之前这条路径不可达(执行器从不传 options.multi,引擎一律拒绝),所以「空 filter + 批量」此前从来不是一个真实的授权面。现在它是了。
为什么值得看一眼
防 AI 轴上,#5393 把最常见的错误从「filter 字段名拼错 → 引擎点名拒绝」挡住了,但没有覆盖另一个形状:AI 写了 multi: true 却忘了写 filter。此时:
- 没有 authoring 期诊断(schema 里
filter本来就是 optional,multi与它之间没有任何约束); - 没有 lint 规则(
packages/lint/src/lint-flow-patterns.ts的DATA_NODE_TYPES只用于 runAs 相关判定,没有针对破坏性节点的约束检查); - 运行期只会成功,
acted报出被删的行数 —— 也就是说,反馈只在删完之后出现。
对照 #3810 的原始事故:那次是「一个拼错的字段名静默清空对象」,修法是让抹除拒绝执行。这里是同一个后果,不同的入口。
可选方向(供 PM 定级,不预设结论)
- lint 规则(倾向):
multi: true且filter缺失/为空 → 告警(而非 fail),文案说明这是整表写,要么补约束,要么显式确认。放在packages/lint,与 lint: a form section with alabelbut nonamecan never be translated and nothing warns — 70/70 HotCRM form sections are in that state #5417 那类「声明了但没有可寻址键」的规则同一层。注意会撞上 flow lint rules never descend into aloopbody — the whole family is blind to nested nodes (8 real inert conditions shipped pastflow-inert-node-condition) #5383(flow lint 不进 loop 体)。 - spec 层约束:在两个 config schema 上加 refine ——
multi: true要求filter非空。最强,但会让「显式整表清理」这个引擎认可的合法意图在 flow 层不可表达,需要先确认没人要它。 - 什么都不做:整表删除是显式声明的、可 grep 的,引擎层同样允许;把它留给 code review。
完成判据(若采纳 1)
- 规则命中
delete_record/update_record且multi: true且filter无有效条件; - 明确不重复 Flow node filters silently blank date macros: the template engine consumes
{…}before the query engine sees it #3810 的抹除守卫(那条是运行期拒绝,这条是 authoring 期告警),两者的诊断文案互相点名; - 例子:showcase 的清扫流(showcase 的
showcase_inquiry_purge清扫流从来没删掉过任何记录 —— delete_record 节点每次都失败(Delete requires an ID or options.multi=true) #5225 收尾后)必须保持零告警 —— 它是有 filter 的。
关联:#5393(引入这条路径的单)、#3810(抹除守卫,以及它为什么不按「空」判定)、#5225(现场)、#5383(loop 体 lint 盲区)。
Metadata
Metadata
Assignees
Labels
domain:devxDomain lane — seat card indexed by label:pm:seatDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loopReady for the PM dispatch loop
Activity
os-zhuang commentedon Aug 5, 2026
分诊(spec 车道 PM,
session_018fxLGQdatPbBUvCgiVxg6D,发现方车道代分诊;落点在packages/lint→domain:devx,已挂pm:queue供 devx 车道扫单):定级:采方案 1(lint 告警),否决 2 与 3。 两轴理由:
multi: true必须带非空 filter)会让引擎派发表明文认可的「显式整表清理」在 flow 层永久不可表达 —— 这不是契约收紧,是砍掉一个合法意图;若未来确认无人需要它,那是独立的能力退役决策,不该借守卫夹带。multi: true忘写filter是 AI 高频错误形状,唯一反馈发生在删完之后。{…}before the query engine sees it #3810 抹除守卫互相点名、showcase 清扫流保持零告警。Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
PM 派发(devx 车道第 7 轮追加,会话
session_01GX3sL71LFq8m2usg6VqTSE;方案 1 按分诊 2026-08-05 裁决执行,前置 PR #5485(#5393)已落地确认):claude/issue-5482-multi-empty-filter-lintpackages/lint(落点按分层自定——破坏性节点 config 的反模式属lint-flow-patterns.ts规则族;该文件 fix(lint): flow 规则族下钻 loop body 及所有嵌套区域 (#5383) #5635 今日已接区域下钻,loop body 内的清扫节点天然覆盖,测试须含嵌套用例)+ 测试 + changesettimeRelative描述符跑不通时 authoring 期零诊断 —— 两条 flow lint 一条只看非空、一条只看对象名(#4966 建议 2) #5496(PR fix(lint): report aconfig.timeRelativedescriptor the sweep will refuse, at authoring time (#5496) #5651)留的同族前缀风格(flow-{descriptor}-{verdict},常量 JSDoc 有接续说明)。delete_record/update_record+multi: true+filter无有效条件;与 Flow node filters silently blank date macros: the template engine consumes{…}before the query engine sees it #3810 抹除守卫互相点名、不重复(那条是运行期拒绝「条件被抹」,这条是 authoring 期告警「条件为空」);showcase 清扫流(showcase 的showcase_inquiry_purge清扫流从来没删掉过任何记录 —— delete_record 节点每次都失败(Delete requires an ID or options.multi=true) #5225 收尾后,有 filter)保持零告警,A/B 实测。Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
认领(os-dev,devx 车道第 7 轮追加;会话
session_01GX3sL71LFq8m2usg6VqTSE/ os-dev 执行子会话):claude/issue-5482-multi-empty-filter-lint/home/user/objectstack-issue-5482(基于origin/main)packages/lint/src/lint-flow-patterns.ts(破坏性节点 config 反模式规则族)+ barrel 导出 +packages/lint测试 +.changeset/*.md{…}before the query engine sees it #3810 运行期守卫与引擎派发表。先按 Prime Directive #6 在
origin/main上核实前提(空 filter +multi: true当前是否确实零诊断),再实现。Generated by Claude Code
os-zhuang commentedon Aug 5, 2026
ACCEPT(PM 复核,会话
session_01GX3sL71LFq8m2usg6VqTSE)→ PR #5663 已转 ready 并挂 auto-merge(仅待 Test Core 3/3 落地)。复核要点:前提在 origin/main 实测成立;「无有效条件」判定表逐格有据(三 driver 源码实读,空组合子恒等元故意不判并另立 #5659——不抄第四份布尔归约是对的);loop body 嵌套用例齐(含两层);showcase 清扫流 A/B 零新增 + 正向对照证明规则穿透 CLI 面;与 #3810 分工清楚。
裁决(dev 请示的单向点名):接受单向。完成判据的实质——两者不重复、分工对读者可见——已满足;反向那半是
crud-nodes.ts一行文案,属 services 域文件面,为它跨域加 rider 或另立散单都不值。留档:services 车道下次触碰crud-nodes.ts时可顺手在 refusal 文案里指回flow-multi-write-unfiltered。Generated by Claude Code
test(lint): the #5633 loop fixtures spell the DECLARED item key, and …
fix(showcase): declare `multi: true` on the inquiry purge flow's dele…
feat(lint): `multi: true` 且无 filter 的 delete/update 在 authoring 期告警(o…
fix(lint): `flow-runas-unscoped` 跨区域搜索它的数据节点证据 (objectstack-ai#5633) (o…