Typed non-replay promotion
一句话
WASM 只有在 Host 能用 typed outcome 证明 workspace 和 external effect 都是 not_started 时,才允许创建一次绑定 parent decision 的 native child;普通异常、timeout 或可能已产生 effect 的失败必须停下。
先看一个例子
Host 已生成一份 initial WASM decision。WASM backend 在 workspace acquire、Guest 创建和 capability dispatch 之前,又返回 Host-authored UnsupportedRunError,指出 exact request 所需 feature 当前不受支持。
如果此时还没有 acquire workspace,也没有 dispatch capability,Host 可以说:“这次 attempt 确定没有开始,换 native 不会重复任何东西。”
另一种情况是程序已经向远端服务提交请求,随后 timeout。换 native 再跑一次也许会提交第二遍。错误文字相同,安全含义完全不同。
真实机制
Initial placement 选择 WASM 后,Orchestrator 只识别 Host-authored UnsupportedRunError。当前真实 Wazero producer 是 Guest 前的 requirement admission;普通运行时异常不会产生这个类型。正常 router 对显式 requirements 应在 L1 直接选择 native,因此 L2 promotion 是 Experimental 的 bounded catch,不是运行中发现任意 incompatibility 的通用路径。该错误必须转换并验证为一份 exact request-bound outcome:
schema_version: 1
kind: runtime_unsupported
escalation_required: true
escalation_reason: required_features_unsupported
required_features: [...]
workspace_disposition: not_started
effect_disposition: not_started
evidence.request_sha256: sha256:...
这份 outcome 不是 Guest 自己打印的字符串。只有全部字段和 request digest 匹配,native backend 也 available,Host 才生成 child decision:
parent: initial WASM decision
child backend: native
reason: l2_not_started_promotion
parent_decision_id: parent identity
Native child 是一次新的 physical attempt,不是旧 Guest continuation。它使用新的 execution ID、container、RPC channel 和 terminal evidence;只有配置了 workspace 时才会 acquire 新 lease。Child 失败后不会再回 WASM,也不会循环 retry。
以下情况不能自动 promotion:
- Python 抛出普通
ImportError,即使文本写着“请改用 native”; - Guest response malformed、普通 runtime error 或非零状态;
- Timeout/cancel 后 Host 不能证明 handler 没有开始;
- Workspace 已被 acquire 或修改;
- Capability dispatch 已 commit,结果为 error 或 ambiguous;
- Provider 可能已完成 effect,但 response 丢失。
这些失败没有 not_started 证明。自动重放会把兼容性 fallback 变成副作用重复执行。
Pure source-pass 的 original-source fallback 看起来也像“换一条路径”,但时点不同。Plugin disabled、not_applicable 或 transform error 都发生在 Agent formal execution 与 authority-bearing work 开始前,所以 Host 可以执行 unchanged original request;derived execution 一旦开始,失败后也不 replay 原程序。这个 pre-execution selector 不是 WASM→native promotion,没有 parent/child placement attempt,也不能把它推广成 post-effect retry。
另一个容易混淆的场景是:某次 native Run 完成并彻底 cleanup 后,下一份新 request 只携带 portable JSON value。Host 可以重新运行 placement 并选择 WASM。这是 fresh backend selection;若请求仍依赖 native process、workspace Ref 或 opaque state,state class 会继续强制 native。系统没有把旧 native heap 或 process 迁移进 WASM。
为什么重要
技术上: Promotion 的合法性由 typed lifecycle fact 决定,不依赖异常名称。Parent/child identity 也让 evidence 能回答“为什么发生了第二个 physical attempt”。
产品和业务上: 这个 Experimental seam 展示了兼容性 fallback 怎样避免把不确定失败变成静默重复扣费、重复发布或重复写入。无法证明安全时,系统宁可报告 error/ambiguous,让 reconciliation 或人来决定。
不能推出什么
unsupported本身不够;workspace 与 effect 还必须同时是not_started。- Timeout、cancel 或 error 不代表外部操作一定没有发生。
- Native child 不继承旧 WASM state,后续 WASM 选择也不继承旧 native state。
- Receipt/evidence 记录 Host 所见,仍不是远端系统的最终事实证明。
- Source-pass transform 前的 original-source fallback 不表示 derived execution 或 capability effect 之后也能安全重放。
术语卡
- Promotion:从初始 WASM decision 创建一次 native child attempt。
not_started:Host 能证明相关 workspace/effect 尚未开始的 typed disposition。- Parent decision:产生 promotion 之前的 exact placement decision。
- Child attempt:绑定 parent identity、拥有独立 execution lifecycle 的新尝试。
- Non-replay:无法证明未开始时,不自动重放可能含 effect 的执行。
继续阅读
- 前置:Artifact、Profile 与 Placement
- 后续:与 EAGER 比较及优化经济性
- 说明文档:
claims/innovation-inventory.md、implementation/07-backends-package-profiles-and-placement.md - 源码 owner:
runtime/outcome.go、runtime/placement/placement.go、runtime/verification/native.go