fix(notify): M298 r1 —— toast 退场腿接上触发点,四条 nit 一并收

复审 P2-1:进场腿自己续帧所以能跑,但进场结束 view_dirty 回 false,
3.6s 后 remaining_ms 掉进 150ms 退场窗口时没有任何代码重新置脏 ⇒ 那一段
收缩从不发生,expire 一抹胶囊整块消失(复审探针:120 拍后 progress 仍 1.0)。

- update/clock.rs:expire 之后用无副作用的 ToastTracks::needs_frame 置脏,
  落到既有的 if view_dirty { refresh_view() } 上采样;refresh_view 仍是门控,
  没有变成每帧无条件调用(静止态判据不变)。
- state/toast.rs:进出两条腿改成**同一个时钟**(寿命 = 首次看见时的
  remaining_ms,已活 = 寿命 - remaining),去掉 Instant 与节拍的二时钟错位;
  新增 needs_frame;新增两条真节拍用例(退场腿必被抽到 / 到期后不再续帧)。
- N1:装配失败与 spawn 失败也用 publish_before_start 的返回值填 session。
- N2:m298 用例注释改准(它抓不住删守卫,那条由 a_report_from_another_session 守)。
- N3:before_start 文档不再把两种来源都说成「进程没起来」。
- N4:refresh_view 末尾的死赋值 view_dirty = false 删掉。

红灯原文(摘掉那 3 行触发点,其余为最终代码):
  退场窗口内必须真的开始收(remaining=Some(40)ms, progress=1)
This commit is contained in:
AskaEth committed 2026-11-13 13:50:41 +08:00
1 parent aa25583806
commit 3b4f8bb997
6 files changed
+190 -58

No files matched your search

+5 -2
View File
@@ -146,8 +146,11 @@ fn a_report_from_another_session_is_never_reused() {
/// 没有会话的两个出口(装配失败 / spawn 失败)**照旧要弹**:那里空串是事实,守卫不该拦它。
///
/// 抓得住:为修「收尾失败被误判」而把守卫整条删掉 / 或把空串也编一个 id 出来(后者会让
/// 装配失败的现场指向一个不存在的会话)。
/// 抓得住:把「没会话」也编一个 id 出来(那会让装配失败的现场指向一个不存在的会话);
/// 也抓得住这条出口被新判据误伤(例如把「session 为空」当成「必须不弹」)。
///
/// **抓不住「把并发守卫整条删掉」**——复审的变异 C 实测:删掉守卫后本用例照样绿(它本来就
/// 走 session 为空的路径)。那条守卫由 `a_report_from_another_session_is_never_reused` 守。
#[test]
fn a_session_less_failure_still_pops() {
let _slot = slot_guard();