Skip to content

fix(showcase): declare multi: true on the inquiry purge flow's delete node (#5225) - #5534

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-5225-showcase-purge-multi
Aug 5, 2026
Merged

fix(showcase): declare multi: true on the inquiry purge flow's delete node (#5225)#5534
baozhoutao merged 2 commits into
mainfrom
claude/issue-5225-showcase-purge-multi

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #5225

前提复核(先证后改)

按 os-dev 规程,动手前先对 origin/main 核实 issue 前提 —— 两条都成立:

第 3 轮 dev 的 needs_decision 遗产(两条探针 byte-identical 失败记录、C 方案否决
理由)按派发口径直接沿用,未重做。

改动

purge 节点声明 multi: true,保留 filter:

config: { objectName: 'showcase_inquiry', filter: { status: 'closed' }, multi: true },

这是「补一个声明」,不是改写流程。#5393 之前节点 config 上根本不存在任何批量意图的
拼写,这正是第 3 轮分诊拒绝 get→loop→逐 id 删的原因(PD #5 workaround)。

⚠️#5482 联动警示,filter 在这里不是修饰:multi: true 而 filter 缺失/为空 =
声明式整表删除。本节点是「批量意图 + 谓词边界」的参考样本,也是 #5482 lint 规则未来
的「必须零告警」验收样本。

同时核过清扫流内没有其它谓词批量节点;全 app 另外 3 个 update_record 节点都用标量
filter: { id: '{...}' } 点名单行,不需要也不应该声明 multi

验收:真机 boot 复跑两条探针

pnpm dev -- --fresh -p 39457,admin 登录后打 05:15Z 记录的同样两条路径。

探针 1 —— 声明式端点 POST /api/v1/apps/showcase/inquiries/purge

success: True | error: None
selected: 2 | acted: 3 | skipped: 1
    purge  delete_record  status=success runs=1 failures=0 acted=2
    report notify         status=success runs=1 failures=0 acted=1

行数实测 4 → 2:两条 closed 行真删,两条非 closed 行(new / contacted)原样存活 ——
filter 确实兜住了删除范围。

探针 2 —— 内建触发路由 POST /api/v1/automation/showcase_inquiry_purge/trigger

success: True | error: None
selected: 3 | acted: 4 | skipped: 1
    purge  delete_record  status=success runs=1 failures=0 acted=3

行数实测 5 → 2(3 条 closed 全删)。两条路径均 acted > 0,原失败签名消失。

反向验证(方向先判后跑)

预判:去掉 multi: true恰好回到 issue 记录的原始失败签名。去掉后重启真机复跑
两条探针,结果与预判一致、逐字相同:

success: False
error: Node 'purge' failed: delete_record(showcase_inquiry) failed: Delete requires an ID or options.multi=true
selected: 1 | acted: 0 | skipped: 0
    purge  delete_record  status=failure runs=1 failures=1

行数前后不变。随后已还原声明。

测试

新增 examples/app-showcase/test/predicate-write-bulk-intent.test.ts(17 例)。

该 example 的测试 harness 不 boot 真实 stack(无 @objectstack/verify 依赖,全部测试
读静态元数据),所以真删断言由上面的真机探针承担,example 层加的是静态钉。钉的写法有
两点是刻意的:

  1. 不是只钉 purge 一个节点,而是把规则陈述为覆盖全部 delete_record /
    update_record 节点的双向不变量 —— 谓词写必须声明 multi: true;multi: true
    必须带非空 filter(multi: truefilter 为空的 delete_record / update_record 是「按声明清空整个对象」,authoring 期零诊断 —— #3810 的守卫按「条件被抹掉」判定,不按「条件为空」判定 #5482 的那一面)。只钉一个节点的话,将来有人加第二个谓词写时
    它会以「什么都没查」的方式继续绿。
  2. 深走 ADR-0031 结构化容器showcase_task_crm_synccatch 区里就藏着一个
    update_record,只扫顶层 flow.nodes 会漏掉它 —— 正是这个守卫要防的那一类缺陷藏在
    下一层。走法对对象图通用,不枚举 try/catch/body 之类容器键名,免得新容器形状
    悄悄落在覆盖外;并单钉 record_failure 必须被收集到,防止回退成平扫。

节点 config 走真实 spec schemasafeParse:此处判的是的裁决(multi
truefilter 为非空谓词),不是「键是否可写」,所以要求 full parse green 才是对的档位。

pnpm --filter @objectstack/example-showcase test
  Test Files  13 passed (13)
       Tests  144 passed (144)

pnpm --filter @objectstack/example-showcase typecheck
  (tsc --noEmit,无输出)

pnpm --filter @objectstack/example-showcase validate
  ✓ Validation passed (1160ms)   EXIT=0
  50 条告警全部为存量(approver 空编制、dead property、权限集等),无一条指向 purge 节点

静态反向验证同样先判后跑:去掉 multi: true恰好 2 例转红(sweep 规则 + purge
专项断言),而 parses green 一例保持绿 —— 因为 multi 按设计是 optional,不写它是
合法的、刻意的选择。也就是说这条回归在执行层,静态层由 sweep 规则兜住,测试里把
这个方向明写了下来,没有硬套「改前绿/改后红」的模板。

范围

examples/app-showcase/** + changeset,未动 packages/**(spec/执行器已就位)。

changeset 按 #5438 先例:private 包,空 frontmatter,不发布任何包。

无越界发现需要另行立项。


Generated by Claude Code

claude added 2 commits August 5, 2026 15:47
…te node (#5225)

`InquiryPurgeFlow`'s `purge` node deletes by the predicate
`{ status: 'closed' }` but declared no bulk intent. The data engine accepts a
write without `options.multi` only when `filter` names ONE row by a scalar
`id`, so every run of this flow failed on that node:

  Node 'purge' failed: delete_record(showcase_inquiry) failed:
  Delete requires an ID or options.multi=true

with `acted: 0` — identically on both paths, the declarative endpoint
`POST /api/v1/apps/showcase/inquiries/purge` and the built-in trigger route
`POST /api/v1/automation/showcase_inquiry_purge/trigger`. The delete half of
the CRUD quartet `src/coverage.ts` claims this flow demonstrates had therefore
never executed once (declared != enforced, PD #10); #5112's boot probes are
what finally reached it.

The fix is a DECLARATION, not a rewrite. Until #5393 (PR #5485) no spelling of
bulk intent existed on the node config at all, which is why the third triage
round correctly refused a get-then-loop-then-delete-by-id rewrite as a PD #5
workaround and escalated instead. `filter` stays: `multi: true` with an absent
or empty filter is a declared whole-object delete, and this node is meant to be
#5482's zero-warning sample for exactly that distinction.

Verified on a real `--fresh` boot, both probes, with row counts:

  endpoint  selected 2 -> purge acted 2, success true, 4 rows -> 2 (both
            `closed` rows gone, both non-closed survived)
  trigger   selected 3 -> purge acted 3, success true, 5 rows -> 2

Reverse-verified by stripping the declaration and rebooting: both probes
returned to the byte-identical original failure above with `acted: 0` and the
row count unchanged.

The new example test states the rule as a two-sided invariant over EVERY
`delete_record` / `update_record` node rather than asserting one node, and
walks ADR-0031 structured containers — `showcase_task_crm_sync`'s `catch`
region holds an `update_record` a flat scan of `flow.nodes` misses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016FNvXhtSdnEGEfLEsMmvxh
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 5, 2026 3:49pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Aug 5, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review August 5, 2026 15:51
@baozhoutao
baozhoutao enabled auto-merge August 5, 2026 15:53
@baozhoutao
baozhoutao added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 5e70c7c Aug 5, 2026
23 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-5225-showcase-purge-multi branch August 5, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants