Outbox在哪个崩溃窗口救了你?
明确提交与网络投递不能靠同一普通数据库事务自动绑定。
合成教学数据 · 未运行真实模型静态推演与完整规格
I13 Outbox在哪个崩溃窗口救了你?
关联章节:M06 · 交互类型:crash-timeline · 状态:提供完整状态/验收规格,UI待实现
教学目标
明确提交与网络投递不能靠同一普通数据库事务自动绑定。
初始状态
{
"run": false,
"outbox": false,
"broker_messages": 0,
"sent": false
}
操作控件
提交Run+Outbox
发送消息
标记sent
任一步注入崩溃
恢复发布器
切换无Outbox方案
状态变化规则
事务提交同时产生run和outbox;事务前崩溃两者都不存在。
发送成功未mark_sent后恢复允许再次发送同一message_id。
消费者按稳定标识去重,最终只启动一个逻辑Run。
黄金案例与反馈
after-db
输入:
[
"commit",
"crash",
"recover"
]
期望:
{
"run": true,
"outbox": true,
"eventual_dispatch": true
}
恢复能发现待发消息。
after-publish
输入:
[
"commit",
"publish",
"crash",
"recover"
]
期望:
{
"broker_messages": 2,
"logical_runs": 1
}
重复交付不是Outbox失效;消费者需要幂等。
no-outbox
输入:
[
"save_run",
"crash_before_publish"
]
期望:
{
"dispatch_guaranteed": false
}
仅写run后“等会发消息”会丢工作。
完成条件
找出丢消息窗口
解释为什么仍可能重复
反馈层次
提交预测后显示结果;允许跳过预测但记录跳过,不阻止阅读
结果同时显示状态变化、原因与被拒绝动作;不是只给红绿灯
第一次错误给边界提示,第二次提供最小反例;参考推导始终可主动打开
重置恢复固定初始状态并保留历史尝试供用户手动清除
无障碍与静态替代
全部操作有原生button/input/select替代,不依赖拖拽
图与动画提供同步文字状态表;支持prefers-reduced-motion
状态改变用aria-live=polite摘要,不逐字朗读长日志
焦点不跳走;错误提示关联输入;颜色不是唯一编码
无JavaScript时直接展示初始输入、规则和黄金案例表,读者仍能完成纸面推演。状态动画仅作解释,不能成为获取答案的唯一途径。
持久化与重置
使用版本化键agent-course:1:I13,仅保存本地学习状态。支持单卡重置和全站清除;存储失败时以内存模式继续,不阻断阅读。
必须避免的误导
不把Outbox说成exactly-once消息系统。
执行边界
纯本地教学状态机;不执行任意代码,不连接真实资金/模型/宿主Shell。