Skip to content

feat(spec)!: retire waitEventConfig.timeoutMs / .onTimeoutwait never had a timeout (#4158) - #4198

Merged
os-zhuang merged 3 commits into
mainfrom
claude/console-screen-flow-submit-jfpjy4
Jul 31, 2026
Merged

feat(spec)!: retire waitEventConfig.timeoutMs / .onTimeoutwait never had a timeout (#4158)#4198
os-zhuang merged 3 commits into
mainfrom
claude/console-screen-flow-submit-jfpjy4

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #4158。按拍板的「撤回」路线做,不是「实现」。

两个键,谎的方式不同

声明的 实际的
onTimeout 'fail' | 'continue',「等待超时时的行为」 零读取者。没有任何路径检查过它,所以 failcontinue 都从未发生;而 .default('fail') 还把一个无人做出的决定盖在了每个 wait 节点上
timeoutMs 「最长等待时间(毫秒)」 唯一的读取者在 timerDuration 缺席时把它当定时时长用。它做了事,只是和名字不符

合起来它们声明了一个 wait 并不具备的超时:运行只在定时器到点或信号到达时恢复,从不在 deadline 上恢复。showcase 自己写着 onTimeout: 'continue',什么也没做 —— 本 PR 一并删掉。

真正的超时语义刻意保持未实现。它应该在有人真的要它时按需求来做,而不是为了迁就两个碰巧被声明的键去反向补一个实现。

timeoutMs 是搬走而不是删掉

因为它确实做了那件事。搬的时候必须 stringify:目标 timerDurationz.string(),而 timeoutMsz.number(),且 parseIsoDuration 把裸数字串读作毫秒 —— 所以 timeoutMs: 60000timerDuration: '60000' 是同一个等待。

直接搬数字会产出一个不再能 parse 的块,这和 #4161eventType 必填那个陷阱同类。有测试专门钉住转换结果仍能 FlowSchema.parse

timerDuration 已存在时则丢弃而非搬移 —— 那种情况下执行器的 ?? 根本够不到它,它早就是死元数据。

为什么两个都退出加载路径

这是注册表已经在用的划分,我照着走:

  • 保留加载窗口 = 键只是被改名了。为一个从没警告过作者的拼法去惩罚他们没有意义。(crud object、notify 别名、sharing full
  • 退出加载路径 = 键描述错了自己。静默吸收它,等于让作者继续相信自己配置了一个超时。(api.requireAuth、tool/app/flow 的 inert keys、RLS priority

两个键都属于后者。迁移链会机械地转换存量源;schema 墓碑给出处方。

三个既有测试被翻转,而不是删掉

flow.test.ts 里三个测试原本断言相反的事:两个键能 parse、onTimeout 默认 'fail'。它们编码的是一个运行时从未兑现的契约。

所以 accept 用例去掉退役键,另加一个显式断言 —— 拒绝而非静默剥离,并检查错误里能找到 4158(处方可被定位)。那一段的 TSDoc 写明了它们原本断言的是什么、以及为什么翻转本身就是要点。

harness 自己抓到的一个交互

#4045 那个 lift 的 fixture 用 waitEventConfig.timeoutMs 演示第四条账本项,而 fixture harness 用 includeRetired 回放整张表 —— 于是它的 after 描述了一个协议 18 之后不可达的终态。改成提升 eventType,用意(一个提升、一个被遮蔽)不变。

验证

结果
spec 全量 274 文件 / 7144 测试通过
service-automation 全量 460 通过
转换 + 迁移 102 通过(本 PR 新增 5)
check:generated 8 个产物全部最新
根级闸门 全 PASS

check:generated 首次实战

刚合入的 #4183 立刻兑现了两件事:

  1. 一次报出 2 个过期产物api-surface.json + content/docs/references/**)。CI 顺序执行只会先报一个 —— 那正是我在 fix(automation,lint,spec): validate the expression slots a node's configSchema declares (#4027) #4040fix(spec,service-automation): the wait executor reads its declared contract only (#4045) #4161 上各多推两轮的原因。
  2. 当场识破 api-surface 是过期-dist 幻影:它就地打印了「这个闸门读构建产物」的说明,重新构建后自行消失,只剩 check:docs 是真过期。--fix 于是只重生成了那一个。

一处既有的文档渲染问题,非本 PR 引入

嵌套的退役键在生成参考里渲染成 timeoutMs?: any,读起来像「什么都能传」,与「已移除」相反(顶层退役键能带上 [REMOVED] … 说明)。

这是既有行为validateOnly?: any 今天就在 content/docs/references/api/batch.mdx 里 —— 正是我引为同类先例的 #4052 那个退役键。本 PR 只是又加了两例,未在此处修,记录备查。

🤖 Generated with Claude Code

https://claude.ai/code/session_01QoA8AV99Ss1RRkLLAYmDzq


Generated by Claude Code

…t` never had a timeout (#4158)

Both keys described a timeout and neither delivered one, so protocol 18 removes
the pair rather than leaving a promise the runtime does not keep (PD #10).

`onTimeout` had ZERO readers. No path ever inspected it, so neither 'fail' nor
'continue' ever happened — and its `.default('fail')` stamped a decision nothing
made onto every wait node. The showcase set `onTimeout: 'continue'`, which did
nothing; it is removed here.

`timeoutMs` said "maximum wait time before timeout" while its only reader used it
as the timer DURATION when `timerDuration` was absent. It did something, just not
what it claimed.

Together they declared a timeout `wait` does not have: a run resumes when its
timer elapses or its signal arrives, never on a deadline. Real timeout semantics
are left unimplemented deliberately — they should be built to a requirement, not
retrofitted to fit two keys that happened to be declared.

`timeoutMs` CONVERTS to `timerDuration` rather than being dropped, because that
is what it did — stringified on the way, since the target is `z.string()` while
`timeoutMs` was `z.number()` and `parseIsoDuration` reads a bare numeric string as
milliseconds. Moving the number unstringified would have produced a block that no
longer parses; a test pins that. With `timerDuration` already set it is dropped
instead, having been dead metadata the executor's `??` never reached.

Both leave the load path, which is the split the registry already draws: a key
retired for being RENAMED keeps a load window, because punishing an author for a
spelling nobody warned them about is pointless; a key that MISDESCRIBED itself
does not, because silently absorbing it lets the author keep believing they
configured a timeout. `api.requireAuth`, the tool/app/flow inert keys and RLS
`priority` all left it for the same reason.

Three existing tests asserted the opposite — that both keys parse, and that
`onTimeout` defaults to 'fail'. They were encoding a contract the runtime never
honoured, so they are flipped rather than deleted: the accept cases drop the
retired keys, and a new case asserts they are REJECTED with a prescription rather
than silently stripped.

One fixture interaction, caught by the harness itself: the #4045 lift fixture used
`waitEventConfig.timeoutMs` for its fourth ledger entry, and the fixture harness
replays the whole table — so its `after` described an end state protocol 18 makes
unreachable. It lifts `eventType` instead.

Verified: spec 274 files / 7144 tests, service-automation 460, all 8 generated
artifacts current (`check:generated`), root gates pass.

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

vercel Bot commented Jul 30, 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 Jul 31, 2026 4:30am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Jul 30, 2026
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/service-automation, @objectstack/spec.

107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/service-automation, @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

os-zhuang and others added 2 commits July 31, 2026 12:09
Resolves the protocol-18 rationale conflict in `migrations/registry.ts`:
main added the `QueryAST.fields` paragraph (#4196) and this branch added the
`wait` timeout pair (#4158). Both are kept and `conversionIds` is the union.

Ordering is load-bearing. Main's paragraph says "Like the two above it is a
request shape", which is only true while `requireAuth` and `validateOnly` are
the two that precede it — so it stays third and the incoming paragraph goes
fourth, renumbered from "The third of the same kind" to "The fourth".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same conflict, one paragraph later: #4286 appended the joins/windowFunctions
sweep to the protocol-18 rationale while CI was running here.

Both are kept and `conversionIds` stays the union. The incoming paragraph is
appended last, and its opener drops the ordinal — "The fourth of the same kind"
became "The same kind of retirement covers". Protocol 18 is collecting an active
sweep of retirements (#3963, #4052, #4196, #4286, #4158), so a positional
self-reference re-conflicts on every one of them; the ordinal-free opener does
not, and matches the "The same major retires" idiom already in the block.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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

Development

Successfully merging this pull request may close these issues.

wait 声明了超时契约但完全没有实现:onTimeout 零读取者,timeoutMs 被当成定时时长用 —— showcase 自己在依赖它

2 participants