Skip to content

fix(events): Node 按帧序就地开始 emit 方式的监听器调用,补同步等待用例、回复原语测试与文档 - #520

Merged
eric8810 merged 2 commits into
mainfrom
fix/events-frame-order-review
Oct 21, 2026
Merged

eric8810 merged 2 commits into
mainfrom
fix/events-frame-order-review

Conversation

@eric8810

@eric8810 eric8810 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

0.15 发布前事件协议评审(#513 之后)发现的几处问题,在根上修正,并补测试与文档。

1. Node:后一次 emit 的监听器可能先于前一次开始(#513 引入的回退)

#513 之后,Node 在读到回复帧时就把 emit 记为已回复;但 emit 方式的 events.call 不是就地执行(只有 events.elsewhere 是),同步调用等待期间与等待无关的调用被扣到等待结束之后。

场景:监听器 a 在本进程,b 在别处。ctx.emit('t/n', 1) 经宿主;b 卸载;插件做一次同步调用。等待期间宿主依次发来 events.call(a,1)(被扣住)、emit 的回复(就地生效)、events.elsewhere remove(就地)。等待结束后 ctx.emit('t/n', 2) 走快速路径,a:2 先于 a:1,违反 §5.5。

修正(按设计原文“Node / Bun / Python 在处理下一帧之前调用监听器”,与 Go/Kotlin/Swift 一致):

  • session.mjs:inline 可以是 (method, args) => bool;
  • runner.mjs:events.elsewhere 与 emit 方式的 events.call 都就地执行,同步调用等待期间也是。其他调用照旧只放行等待链上的。

2. 一致性用例(只追加)

emit/order-across-fast-path-sync-wait(tool: false,不改 toolCases):发出方 emit 1 之后同步调用 events-log.until("resume");执行器卸载 b、等宿主回复(answered)、再记 resume;发出方随后 emit 2。要求日志 a:1 在 a:2 之前,且 emit 2 走快速路径(viaHost: false)。

为此格式新增:spec 的 then({until, args})、unload 步骤的 then、宿主服务 events-log.until(同步)。各语言夹具(Node Cordis / 叶子、Bun、Python、Go、Kotlin、Swift)支持 then。

修正前 node-leaf 列失败(["until:resume","resume","a:2","a:1"])。修正后本地每一列都过(RUTIS_EVENTS_PLACEMENTS=all):

X 结果
node(Cordis) 通过
node-leaf 通过
bun 通过
python 通过
go 通过
kotlin 通过
swift 通过

整套(sampled)在 node、node-leaf、bun、python、go 上 0 失败。Bun 与 Python 在同步等待期间本来就执行无关调用(可重入),已核实。

3. 回复原语的单元测试

每个运行时两类:回复在处理下一帧之前生效;0.13 宿主回 future 时仍能正确结算。

  • Node:session.test.mjs(invokeAnswered、inline 判断函数在同步等待期间就地执行 emit 方式的调用)、events.test.mjs(总线:普通值立即、future 结算后、失败照报)
  • Bun:session.test.ts(invokeAnswered)
  • Go:internal/peer/answered_test.go(SendAnswered,含 future 成功与失败)
  • Python:test_peer.py(request_answered)、test_events_session.py(下一帧看到已回复;0.13 的 future 结算后才算已回复)
  • Kotlin:EmitReplyTest.kt(sendAnswered)
  • Swift:EmitReplyTests.swift(sendAnswered)

4. 文档

  • 事件协议(中英)第 29 条重写:回复总是普通值,在各调用开始后写出,运行时按帧序就地接收;附 2026-10-21 修订说明与 0.13 兼容。第 30 条起已核对,无矛盾。§5.5 补“同步调用等待期间也是”;§13.2 补 then 与 events-log.until,§13.3 列出新用例。
  • .agents/skills/rutis-new-runtime/references/runtime-contract.md:快速路径段落与消息表补帧序规则。
  • docs/migration-0.15*.md 的“事件”一节(在 docs: 0.14 不单独发布,版本写法统一为 0.15 #517 的段落上扩展):宿主总是回普通值、运行时按帧序接收、answered 等期望与新键、Tap::answered、与 0.13 的兼容。

@eric8810 eric8810 added this to the 0.15 milestone Oct 21, 2026
@eric8810
eric8810 force-pushed the fix/events-frame-order-review branch from ca78fea to 167c62c Compare October 21, 2026 08:33
- Node:会话的 inline 可以是判断函数;emit 方式的 events.call 与 events.elsewhere 一样就地执行,同步调用等待时不再扣到等待结束之后(§5.5)
- 一致性用例 emit/order-across-fast-path-sync-wait:两次 emit 之间有一次同步调用;spec 新增 then、unload 新增 then、events-log 新增同步的 until
- 各语言回复原语的单元测试:回复在处理下一帧之前生效;0.13 宿主回 future 时仍能正确结算
- 协议文档第 29 条与 §5.5、§13.2 修订;runtime-contract 补帧序规则
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant