test(spec): 修掉 flow fixture 教的三种跑不通的形状 (#4924) - #5502
Merged
Conversation
…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.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckNo hand-written docs reference the 0 changed package(s). ✅ |
This was referenced Aug 5, 2026
Closed
os-zhuang
marked this pull request as ready for review
August 5, 2026 15:48
Contributor
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 31022222345 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Aug 5, 2026
This was referenced Aug 5, 2026
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Aug 6, 2026
…s runnable, and pin them (objectstack-ai#5500) (objectstack-ai#5686) Follows objectstack-ai#4924 / PR objectstack-ai#5502 on the same file. Four of the five classes objectstack-ai#5500 listed were real and are fixed; the first class's stated mechanism did not survive verification and is reported instead of forced. - assignment (`assign_output`): `{ variable, value }` -> `{ assignments: { … } }`. logic-nodes.ts normalizes three shapes and its last branch makes top-level config keys the variable NAMES, so the old config declared two variables literally named `variable` and `value` while `contactId` (declared `isOutput: true`) was never written. Measured end to end before the fix. - loop (`loop_records`): legacy flat-graph loop -> ADR-0031 structured container. With no `config.body` loop-node.ts reads `config.collection` as a bare VARIABLE NAME, so `'{get_old_records.records}'` matched nothing, bound nothing and fell through; the `loop -> delete -> loop` back-edge was ordinary traversal and `{item.id}` referenced a variable no one set. The delete node moves into `config.body` and `iteratorVariable` binds `item`. - get_record (`get_old_records`): string `filter` -> record form. The contract declares `z.record(z.string(), z.unknown())`, so the string failed safeParse outright, and `DAYS_AGO()` is implemented nowhere. The date window is spelled `{90_days_ago}` — a spec date macro whose slot ownership is declared: a known filter token passes through interpolateFilter verbatim for the query engine's resolveFilterTokens to expand, and date-macros.zod.ts names "flow node filters" as a consumer. `limit` added because it selects the `find`/`records` branch the loop needs. - `object` -> `objectName` at the three remaining sites (ADR-0087 D2 `flow-node-crud-object-alias`). - edge conditions: `{…}` template braces -> bare CEL (ADR-0032 §1a). Verified that registerFlow rejects the braced form outright, so those fixtures could not register at all. Class 1 ("the `{<node id>.<field>}` output dialect is never bound") is NOT implemented, because it is false: engine.ts writes every node's `result.output` under `<nodeId>.<key>` and template.ts resolves that flat key, which crud-nodes.ts documents as the default binding. `{create_contact.id}` resolves and is kept verbatim. The real defect at the other cited site was narrower — get_record only emits `records` on the `limit > 1` branch — and is fixed above. Each fix carries a contract assertion, and each was reverse-verified by restoring the broken shape. That caught a phantom in the first draft of the brace pin: ExpressionInputSchema normalizes a bare-string predicate into a `{ dialect, source }` envelope, so asserting `toContain` on the parsed `condition` object passed regardless of the predicate. The pins now read `condition.source`. Refs objectstack-ai#5500 Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Aug 6, 2026
* docs(pm-dispatch): 探活入例行、报告丢失直接验收、座位行自查等五处协议更新 2026-08-05 实测教训回写(维护者指示): 1. step 6 新增「探活」固定动作:完成通知在宿主进程重启下会静默丢失—— 三个在飞 dev 死亡数小时无任何信号;每轮巡检对无产出的 dev 发状态 询问,「no active task; resumed from transcript」回包即当场复活。 2. step 6 新增「报告丢失直接验收」兜底:PR 全绿 + agent 确认死亡/静默 ≥2h + 报告未达 ⇒ 直接按 PR 对照 origin/main 验收(objectstack-ai#5550/objectstack-ai#5556 先例)。 3. 座位表协议新增「每轮核对自己的正文行」:协议升级迁移状态可把在任 PM 记成待认领,叠加惰性回收条款即误回收风险(spec 座位当日实例)。 4. worktree 接手协议前置 SendMessage 复活路径(更便宜、保全上下文), 接手协议降为 resume 不可用时的后备。 5. step 7 新增两条评审判据:验收判据本身可被 dev 用测量证伪(objectstack-ai#5452 字面 grep 归零判据不成立,改钉配平不变量);tests/docs-only PR 走 skip-changeset 标签且 rerun 无效需新 labeled 事件(objectstack-ai#5497/objectstack-ai#5502)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D * docs(pm-dispatch): skip-changeset 条目随 objectstack-ai#5625 更新 —— 闸门已实时读标签,教训改记为「载荷 vs 现状」通病标本 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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+recordIdobjectName+filter: { id: … }+outputVariable: 'opportunity'GetRecordConfigSchema两个旧键都不声明;object是 ADR-0087 D2flow-node-crud-object-alias在加载期改写的退役拼写,recordId从来没有 reader。补outputVariable是因为下游谓词引用的opportunity此前根本没被绑过check_amount(decision)config.conditionconfig.condition只有start节点(触发闸门)会读,其余节点类型上惰性 —— 正是flow-inert-node-condition(#4414)报的那件事。DecisionConfigSchema只声明conditionsauto_approve(update_record)recordIdobjectName+filter: { id: … }objectName是执行期必填,缺了 executor 直接refuseNode,所以这个节点原本也跑不起来delete_recordrecordId一个键objectName+filter: { id: '{item.id}' }出边(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、谓词里不含{。验证
反向验证(方向是事先定好的:恢复旧形状应当变红,因为这些契约是
strictObject,旧键会作为 unknown key 被拒):flow.test.ts2 failed(approval flow 断在GetRecordConfigSchema.safeParse(...).success为 false;scheduled flow 断在DeleteRecordConfigSchema)config.condition放回去 → 1 failed:expected { Object (condition) } to be undefinedexpected [ { 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