Skip to content

test(spec): 修掉 flow fixture 教的三种跑不通的形状 (#4924) - #5502

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-4924-flow-fixture-shapes
Aug 5, 2026
Merged

test(spec): 修掉 flow fixture 教的三种跑不通的形状 (#4924)#5502
os-zhuang merged 2 commits into
mainfrom
claude/issue-4924-flow-fixture-shapes

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #4924

背景

packages/spec/src/automation/flow.test.ts 的示范 flow 里,四个节点写着 executor 一个都不读的 config 键。它们全绿,是因为 fixture 断言的是 FlowSchema / FlowNodeSchema,而 FlowNodeSchema.config 按 ADR-0018 刻意是开放的 z.record(z.unknown())(node.type 对插件开放,插件 executor 自带 configSchema)。所以这不是一个会红的测试,是一份会被照抄的教材。

FlowNodeSchema.config 没动 —— 按 issue 明示,它的开放是设计,不是遗漏。

改了什么(四处,各带原地注释)

节点 改前 改后 依据
get_opportunity (get_record) object + recordId objectName + filter: { id: … } + outputVariable: 'opportunity' GetRecordConfigSchema 两个旧键都不声明;object 是 ADR-0087 D2 flow-node-crud-object-alias 在加载期改写的退役拼写,recordId 从来没有 reader。补 outputVariable 是因为下游谓词引用的 opportunity 此前根本没被绑过
check_amount (decision) config.condition 删除,分支落到出边 config.condition 只有 start 节点(触发闸门)会读,其余节点类型上惰性 —— 正是 flow-inert-node-condition(#4414)报的那件事。DecisionConfigSchema 只声明 conditions
auto_approve (update_record) recordId objectName + filter: { id: … } 同上;另外 objectName 是执行期必填,缺了 executor 直接 refuseNode,所以这个节点原本也跑不起来
delete_record 只有 recordId 一个键 objectName + filter: { id: '{item.id}' } 三个里最严重的一个:唯一的「约束」根本不被读 = #3810 的 match-everything delete,穿着一个读起来像约束的键

出边(decision 修复的落点):e3 改成 isDefault: true 的兜底边(与 condition 互斥,同时写会被 flow-default-edge-with-condition 判 error),e4 保留唯一的守卫分支,谓词改写成 bare CEL(opportunity.amount > 100000)—— {…} 模板花括号在 CEL 里是 map 字面量,registerFlow 的表达式校验硬报错,就是 #1491 那个坑。

让它变成一个「会红」的测试

只改 fixture 的话,下一个人照旧可以把 recordId 写回来而测试依然全绿。所以两个 fixture 现在额外把修正后的节点 config 拿去过 executor 运行期真正 parse 的那几份契约(GetRecordConfigSchema / UpdateRecordConfigSchema / DeleteRecordConfigSchema),并断言 decision 无 config、出边恰好一条守卫 + 一条 isDefault、谓词里不含 {

验证

pnpm --filter @objectstack/spec test        →  Test Files 311 passed (311) / Tests 7951 passed (7951)
pnpm --filter @objectstack/spec typecheck   →  tsc --noEmit,无输出
node scripts/check-nul-bytes.mjs            →  OK (5460 tracked text files)

反向验证(方向是事先定好的:恢复旧形状应当变红,因为这些契约是 strictObject,旧键会作为 unknown key 被拒):

  • 三处 CRUD 旧形状全恢复 → flow.test.ts 2 failed(approval flow 断在 GetRecordConfigSchema.safeParse(...).success 为 false;scheduled flow 断在 DeleteRecordConfigSchema)
  • 只把 decision 的 config.condition 放回去 → 1 failed:expected { Object (condition) } to be undefined
  • 只把两条出边放回旧样子 → 1 failed:expected [ { id: 'e3', …(4) }, …(1) ] to have a length of 1 but got 2

三组 pin 各自独立生效,没有一个是靠别的失败遮住的。

关于 changeset

没有加。改动只落在一个 .test.ts 文件里:没有 schema、导出、可授权键或运行时行为的变化,npm 包发的是 dist,消费者拿不到这个文件,CHANGELOG.md 里也无话可说。按 AGENTS.md 的「纯 bug fix 不需要 changeset」,这里连用户可见面都没碰到。

范围外的发现,已单独立 issue

#5500(finding 标签,未指派):同一个文件里还有五类不同缺陷类的形状 —— {节点id.字段} 这套引擎从不绑的输出引用方言、assign_output 因为没有 assignments 包裹而实际创建了两个名叫 variable/value 的变量、loop_records 是 legacy flat-graph loop(所以本 PR 里 delete 节点的 {item.id} 要等它改成 ADR-0031 结构化 body 才真的有值,原地注释已写明)、get_old_records 的字符串 filter(可跑形状需要拍板,没在这里猜)、以及另外三处 brace-CEL 出边条件和两处 object 别名。按 Prime Directive #10 记录,没有在本 PR 里顺手扩范围。


Generated by Claude Code

…4924)

`packages/spec/src/automation/flow.test.ts` demonstrated four flow node
configs whose keys no executor reads. They stayed green because the
fixtures assert `FlowSchema` / `FlowNodeSchema`, and `FlowNodeSchema.config`
is deliberately an open `z.record(z.unknown())` (ADR-0018) — so the file was
teaching material for shapes that cannot run, not a test that could go red.

Corrected, each with an in-place comment saying why:

- `get_record` / `update_record` / `delete_record`: `object` -> `objectName`
  (the ADR-0087 D2 `flow-node-crud-object-alias` spelling), `recordId` ->
  `filter: { id: … }` (CRUD executors locate rows through `filter` only),
  plus the execute-time-required `objectName` the update/delete nodes never
  had. The `delete_record` node had `recordId` as its ONLY key — a
  match-everything delete (#3810) wearing a key that reads like a constraint.
- `decision`: the inert `config.condition` is gone. The key is the trigger
  gate on a `start` node and is read on no other node type
  (`flow-inert-node-condition`, #4414); branching moves to the out-edges —
  one guarded branch plus `isDefault: true` on the fallback.
- the moved predicate is bare CEL (ADR-0032): `{…}` braces parse as a CEL
  map literal and are rejected by `registerFlow` (the #1491 trap).

`FlowNodeSchema.config` is untouched — it is open by design (ADR-0018).

Both fixtures now also parse their corrected node configs against the
per-node-type contracts the executors parse at run time, so re-introducing
any of these shapes fails the test instead of passing it.
@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 2:34pm

Request Review

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 0 changed package(s). ✅

@os-zhuang os-zhuang added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/s labels Aug 5, 2026 — with Claude
@github-actions github-actions Bot added the size/s label Aug 5, 2026
@os-zhuang os-zhuang removed the size/s label Aug 5, 2026 — with Claude
@github-actions github-actions Bot added the size/s label Aug 5, 2026
@os-zhuang os-zhuang added domain:spec and removed size/s labels Aug 5, 2026 — with Claude
@github-actions github-actions Bot added the size/s label Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 31022222345 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (3/3) — 失败步骤: Run this shard's tests(日志不可读,点进 job 看)

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 13 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 在其他 PR 的同类评论里搜同名测试;出现过 ⇒ flaky 实锤,开 issue 修/隔离那条测试。修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 5, 2026
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 9894a72 Aug 5, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-4924-flow-fixture-shapes branch August 5, 2026 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain:spec size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec 自己的 flow fixture 教了三种跑不通的形状:CRUD 的 recordId、decision 的 conditionobject(#4001 第一类发现的第七例)

2 participants