15 KiB
04.runtime_workspace 设计评审(二)
评审编号: 02
日期: 2026-08-06
评审对象: 04.runtime_workspace.md(实施权威)、01.design_review.md、00.base.md、01.kernel.md、03.sdk_and_coreapp.md、plan.md、app_final_design.md、lineup-runtime-sdk-architecture.md、lineup-app-layer-architecture.md。
评审方法: 独立地以第 04 次迭代定义为实现依据,逐项回溯前三次迭代和正式 Runtime 契约;重点检查 instance state / deadline operation 的所有权、pomodoro.start 与用户中断的终态竞争、离开/暗屏/重启、App 子会话与 pending Interact、Whiteboard 范围和 Rust P5 延期。
总体结论: D04-01~D04-09 已确认的方向均已被第 04 次主定义或计划正确吸收:业务快照与 deadline operation 分离、Agent Tool pomodoro.start / pomodoro.interrupt、到点/中断原子终态、暗屏/锁屏不等于离开、子会话只读与 pending Interact 留在主 IM、Whiteboard 不进入本轮闭环、Rust 仅为 P5 后续评估。D04-07 已澄清为 Agent Tool 路由契约;D04-08 已选择“立即收口、由 Interact 呈现完成结果”;D04-09 已选择 retain_readonly 快照保留策略。所有 P0~P3 设计问题已清零,本迭代设计已就绪,待实施。
问题清单(Outline)
状态标记:🔴 未解决,必须处理;🟡 已提出修复方向,尚未确认或未回填实施权威;✅ 已解决;⚪ 可延期但必须保留记录。P0~P3 必须在当前迭代处理;P4~P5 可以延期,但必须记录延期原因和重新评估条件。
| 状态 | 优先级 | 编号 | 问题 | 当前结论 / 下一步 |
|---|---|---|---|---|
| ✅ | P1 | D04-07 | pomodoro.interrupt 的调用方向与目标绑定未明确。 |
已确定为 Pomodoro 在 Manifest 中声明、由 Agent 调用的短 Tool;Runtime 从调用会话绑定当前 focusing operation,负责路由、原子裁决和稳定回执。 |
| ✅ | P1 | D04-08 | completed 后既要求 Pomodoro 显示自身完成提示,又在主流程中要求 Runtime 收口关闭 App、恢复 Interact;没有定义两者的顺序和触发者。 |
已选择立即收口:Runtime 原子完成并投递唯一结果/outbox 后立即关闭 Pomodoro、结束子会话、恢复 Interact;完成提示由 Interact / Agent 呈现。 |
| ✅ | P2 | D04-09 | Pomodoro 要保存 instance snapshot,但没有声明其 app.instance-state.v1 Manifest 配置、schema 版本/字段、配额和 retention 选择。 |
已确定 app.instance-state.v1 最小 Manifest:严格 schema v1、4 KiB 配额、retain_readonly。关闭后旧快照只作历史展示,不可写入、不可复活为 focusing。 |
| ✅ | — | D04-C1 | instance state 与 deadline operation 的事实来源和持久化边界 | 已正确同步;不构成新问题。 |
| ✅ | — | D04-C2 | pomodoro.start、到点/中断竞态和 outbox 唯一性 |
已正确同步;D04-07 补足 Agent Tool 的路由与目标绑定契约。 |
| ✅ | — | D04-C3 | 用户离开、自动暗屏/锁屏、Surface 重载和重启 | 已正确同步;不构成新问题。 |
| ✅ | — | D04-C4 | App 子会话、只读历史和 pending Interact | 已正确同步;不构成新问题。 |
| ✅ | — | D04-C5 | Whiteboard 第 04 次范围 | 已正确同步到主定义和 plan.md;不构成新问题。 |
| ⚪ | P5 | D04-C6 | Runtime Core / SDK 是否迁入 Rust | 已保留为延期项;当前不应扩大到本轮实现。重新评估条件仍为 profiling 证明 Runtime Core 是性能热点。 |
通过项与一致性证据
D04-C1:instance state 与 deadline operation 的边界已一致
主定义第 2.2、2.3 和第 3 节将 Runtime 定为 schema/revision 受控的 instance snapshot 保存者,同时将
ends_at、Tool 终态和 outbox 交给 deadline operation;remaining_seconds 仅由 UI 按绝对截止时间派生。
这与正式方案的明确边界一致:app_scope + instance_id 隔离状态快照,快照不能完成 Tool、写 outbox、改变
焦点或绕过 lifecycle;Pomodoro 的到期/中断和唯一 Tool result 必须由 Runtime deadline operation 裁决。
这也没有把静态 IM/App 子会话误当作 MiniApp 业务存储:第 04 次主定义仍只让子会话记录人与 Agent 的交互和 结果,符合第 03 次“App 子会话不是 App 操作日志”的规则。
D04-C2:Agent Tool 的终态竞争与 outbox 所有权已一致
第 04 次主定义将 pomodoro.start({ duration_seconds, activity? }) 定义为长期 Agent operation,并由 Agent
调用短 Tool pomodoro.interrupt({}) 请求停止当前专注;到点、Agent 中断和关闭竞争一个原子终态,第一成功者
产生一次结果/outbox;重复或迟到的中断只获得稳定幂等回执。
这符合第 03 次对 Tool/交互的“Runtime 原子终态 + 唯一 outbox”模式,也符合正式方案中 App 不能自行完成
Tool 或写 outbox 的限制。D04-07 已补足:用户以语言提出停止意图后,Agent 调用 Pomodoro 声明的 Tool,
Runtime 而非 MiniApp 界面负责最终裁决。
D04-C3:离开、自动暗屏、重启的产品语义已一致
主定义已清楚区分:用户切回 Interact、切到其他 MiniApp 或关闭 Pomodoro 会中断;自动暗屏、锁屏、Surface
重载不会中断;重启按持久化 ends_at 恢复或一次性结算;Runtime/Host 完全未运行时不承诺即时系统提醒。
这不再依赖 iframe/UI 定时器,且没有把来电检测、系统免打扰、静音或系统通知偷偷纳入第 04 次范围。
D04-C4:App 子会话和 pending Interact 已一致
主定义限制“仅恢复同一个未结束 instance 时复用同一个子会话”;结束后子会话只读;关闭时仍 pending 的
Interact 标准交互留在主 IM/Interact 中,回答不得追加到旧子会话。该规则与第 03 次的 instance 生命周期、
pending_interaction_call_ids 和“关闭不自动取消 Interact 交互”规则一致。前台 Pomodoro 上的 Interact
覆盖层也没有改变交互 owner。
D04-C5:Whiteboard 范围已消除冲突
04.runtime_workspace.md 第 5 节与 plan.md 第 3 节均明确:Whiteboard 只保持第 03 次已完成的 SDK/隔离
回归,不接入第 04 次工作区切换或完整用户流程;Pomodoro 才是本轮唯一新增的工作区闭环验证应用。
D04-C6:Rust 为正确的 P5 carryover
主定义明确本轮保持 TypeScript Runtime / Web Bridge 基线,且仅在 profiling 已证明 Runtime Core 是热点后另行 评估。该范围不会破坏 Web Reference Host、sandbox iframe、postMessage Bridge 和 UI 必须保留在浏览器侧的 既有边界。
D04-07:用户中断的 Agent Tool 路由契约
已解决(2026-08-06,用户决议)。
pomodoro.interrupt 不是 MiniApp 通过 SDK 主动发送的 Runtime command,而是 Pomodoro 在 Manifest 中声明、供
Agent 调用的短 Tool。用户主要经 IM / Voice 向 Agent 表达业务意图;MiniApp 是 Agent 的业务工具,而不是
用户直接操作业务状态的独立应用。Tool Router 从该 Tool 调用的 conversation_id 自动绑定当前唯一
focusing Pomodoro operation,调用者不能提供 instance_id、app_scope 或 operation_id。Runtime 路由调用、
在与 deadline/close 相同的原子裁决中将长 Tool pomodoro.start 收口为 interrupted,并给短 Tool 返回
interrupted、no_active_focusing_operation 或 operation_already_final 的稳定回执。若 Surface 不在,Runtime
仍可完成裁决;若存在,只投影最终状态。用户直接离开工作区仍由 Runtime 生命周期中断处理,不伪造 Agent Tool。
以下为发现时、尚未澄清调用方向的风险分析。
第 04 次主定义第 3 节规定用户显式退出通过受限 SDK Runtime command
pomodoro.interrupt(reason = "user_exit") 请求;又规定 Runtime 仅接受当前 focusing instance 的第一次有效
中断,迟到请求返回幂等回执。这个产品语义正确,但尚不足以决定 SDK/Bridge、Web Host 和 Tauri Host 应如何
实现同一动作。
现有正式 SDK 的唯一通用出站入口是 actions.dispatch(action: AppAction),但没有定义 AppAction 的
schema、Command type、由 context 派生的 instance 绑定、receipt 格式或稳定拒绝码。第 03 次冻结的 Manifest
也只定义 Agent Tool(direct / launch / foreground / operation)和 AppNavigationAPI.close;它没有把
业务 MiniApp 任意命令自动变成合法 Agent Tool。若不冻结该层,至少会出现两种相互冲突的实现:MiniApp 用
sdk.tools.complete/fail 伪造 pomodoro.start 终态,或 Web/Tauri Host 在 UI 侧直接关闭实例;两者都绕开
了 Runtime 的 deadline/outbox 原子裁决。
建议在主定义中明确一个最小、仅 Runtime 可执行的 command,例如概念上:
type PomodoroInterruptCommand = {
type: "pomodoro.interrupt";
reason: "user_exit";
// instance_id、app_scope、conversation_id 从不可伪造 SDK context 推导,调用者不得传入。
// Runtime 从当前 instance 关联的 operation / source_tool_call_id 查找目标。
};
type PomodoroInterruptReceipt =
| { accepted: true; operation_id: string; state: "interrupted" }
| { accepted: false; code: "operation_already_final" | "operation_not_focusing" | "instance_closing" };
实际字段命名可以不同,但必须同时冻结以下规则:调用者只能操作自己的当前 instance;谁提供或由 Runtime
生成幂等键;focusing、deadline、closing 与已终态各自的稳定回执;以及 command、deadline 与
AppLifecycleManager.close 如何在同一持久化事务或等价 compare-and-set 中竞争唯一终态和唯一 outbox。
Host 的“返回/切换/关闭”应调用同一个 Runtime 内部操作,而不是从 Surface 绕过该 Command。
D04-08:完成提示与关闭的顺序未定义
已解决(2026-08-06,用户决议:方案 B)。 Runtime 到达 ends_at 后,先原子写入 completed、唯一
pomodoro.start 结果与 outbox;随后立即关闭 Pomodoro、结束 App 子会话并恢复 Interact。Pomodoro 不显示
独立的完成提示;完成结果由 Interact / Agent 呈现。关闭后的 outbox 重试不依赖 Pomodoro Surface,且不得再次
完成 Tool。完成与关闭并发时,已提交的 completed 终态不得被 cancelled(app_closed) 改写。
以下为决议前的风险分析。
主定义同时作出了两项要求:Pomodoro 到时间“显示自己的完成提醒”(第 2.3、3、4.4 节),迭代目标的主流程又
写为“到时间显示完成提醒并回传结果 → Runtime 收口并关闭 App,恢复 Interact”。但它没有规定完成态是否先
投影给仍存活的 Surface、提示展示多久或由谁确认、何时调用 AppLifecycleManager.close、以及 App 子会话在
哪个时点结束。
这是可观测行为的分歧,不是纯 UI 细节。若 Runtime 原子完成后立即按一般收口关闭 instance,Surface 会先被
卸载,Pomodoro 无法兑现“App 内完成提示”;若 UI 本地自行显示后再关闭,则可能在重启、旧 Surface 或重复
回调下与已提交 outbox 脱节。第 03 次的既有规则还区分“Tool 完成不自动关闭 App/子会话”和
AppLifecycleManager.close 才结束子会话;第 04 次为 Pomodoro 采用不同的自动关闭策略是允许的,但必须
明确这是 Pomodoro 的特例及其顺序。
建议二选一并写入工作内容、恢复规则和验收:
- 保留完成展示窗口。 deadline operation 原子写
completed和唯一 result/outbox,Runtime 将终态投影给 Pomodoro;Surface 在一个明确的受控窗口内显示完成提示;窗口结束、用户确认或用户离开后由 Runtime 发起AppLifecycleManager.close,再结束子会话并恢复 Interact。重启落在窗口内时须能按持久化终态恢复到同一 收口路径,而不得再写结果。 - 立即收口。 Runtime 完成后立刻关闭 App、结束子会话、恢复 Interact;删除“Pomodoro 显示自己的完成 提示”,或将完成提示明确改为 Interact 的可信呈现而非已关闭 MiniApp 的 UI。
无论选择哪种,验收都应覆盖 completed 与 close 并发、已提交 outbox 的重试、终态 UI 不产生第二次 Tool
完成,以及完成前/后 pending Interact 的归属。
D04-09:Pomodoro 的 instance-state Manifest 与关闭后保留策略未确定
已解决(2026-08-06,用户决议:方案 B)。 Pomodoro 声明 app.instance-state.v1,使用严格的 schema
version 1、4 KiB 配额和 retain_readonly。快照只保存 UI/历史展示所需的活动、关联 operation、时间和展示
状态;operation 仍是终态、deadline 与 outbox 的唯一事实来源。App 关闭后,旧快照仅供只读历史展示,不能
再写入、不能恢复为 focusing,也不能作为新一轮专注的运行状态。
以下为决议前的风险分析。
主定义要求 Pomodoro 通过 SDK 保存展示所需的 instance snapshot,并列出可能字段;正式 Runtime 契约则规定
只有声明 app.instance-state.v1 的 Manifest 才能使用 schema/revision 受控状态,并要求声明
state_schema_version、state_schema、max_bytes 和 retention。第 04 次定义尚未选择 Pomodoro 的实际
Manifest 配置,也没有说明关闭后应使用默认 delete_on_close 还是 retain_readonly。
这会直接改变重启/关闭后的可见行为和验收:delete_on_close 允许完成或中断时清理 UI snapshot,而历史由
App 子会话和 Tool/operation 记录承载;retain_readonly 会保留一个不可写旧 snapshot,只能作为历史或新
instance 的受控上下文。两者都可以符合产品目标,但实现、fixture 和隐私/清理预期不同;不能靠默认值隐式
决定。
建议主定义补一份 Pomodoro 的最小 Manifest 片段或等价表格:required feature、严格 schema(至少允许的展示
字段)、schema version、最大大小和明确 retention。还应说明完成/中断/关闭时 instance snapshot 与 deadline
operation、App 子会话、Tool/outbox 的独立清理顺序,确保“保留 UI 状态”不会被误解为旧 instance 可恢复为
focusing 或可再次写入。
评审关闭条件
- D04-07~D04-09 已回填本轮实施权威 04.runtime_workspace.md 和 plan.md;实施时须依照已冻结的 Tool、完成收口和 Manifest 契约提供 fixture 与自动化验收;
- D04-C6 继续作为 P5 carryover 留在本记录与第一轮记录中;没有 profiling 证据前,不把 Rust 迁移纳入当前 实现范围;
- 本评审只记录发现和建议,未修改第 04 次主定义、正式方案、计划或实现。