Skip to content

Latest commit

 

History

History
74 lines (49 loc) · 7.13 KB

File metadata and controls

74 lines (49 loc) · 7.13 KB

ADR-031: 采集暂停来源状态机与前台自动暂停

状态

已接受(2026-09-09 用户逐项授权;v0.20.7 批 2a/2b 交付)

日期

2026-09-09

背景

ADR-013 决策 2(播放暂停驱动 = 复用 A1 共享暂停标志;手动暂停全冻结 vs 自动暂停轻量轮询)落地后,用户实测暴露三类问题:

  1. 暂停延迟:pause 只置共享布尔,live:paused 事件与引擎 reset 全在主循环 500ms 轮询边沿执行,边沿 flush 内同步跑 SenseVoice 整句重打分(有界 3s/句)——点击后 UI 数百 ms~3s+ 才翻转;
  2. 快速恢复丢内容:暂停-恢复落入轮询间隙时边沿不可见(引擎不 reset、无事件记录);恢复侧无 flush/边界处理,pending_merge 链式合并无暂停边界判断,会跨暂停错误合并——半句切碎/尾音丢失;
  3. UI 状态回退:媒体自动暂停(worker 直写标志 + live:media-paused)与手动暂停(主循环 live:paused/resumed)双轨并存、无来源区分;前端课堂页/浮窗/徽标各自维护状态且挂载拉取曾忽略 paused 字段——事件丢失/重挂后 UI 显示空闲。

另有用户新需求:被采集目标窗口离开前台时自动暂停、回前台自动恢复(区别于仅键盘失焦;熵减自身窗口与软件内切页不触发)。

决策

  1. 暂停状态升级为带来源的单状态机(pause_state.rs):PauseSource = Manual | Media | Foreground;manual 为锁存层(持锁期间 auto 提议只记条件不动作,手动解除瞬间重评估 auto 条件),auto 层各条件互不解除、只解自己;PauseShared{paused, total_paused_ms, seq, edge 槽, reason},request API 单写入点;total_paused_ms 写纪律不变(仅捕获线程补偿时长)。
  2. 暂停边沿语义修订(取代 ADR-013 决策 2 中"边沿 flush 尾句 + 引擎 reset 共用停止路径 flush"的重打分行为):暂停边沿用 flush_no_rescore()(跳过 SenseVoice,质量由恢复侧去重与边界保护兜底;停止路径保留重打分);恢复边沿 flush + 去重边界保护 + 按暂停边界切断链式合并状态。
  3. 漏边沿代数补偿:seq 每完成一个物理暂停区间 +1;主循环按 seq 跳跃检测漏边沿,plan_edge_observation 纯函数判定(own_pending 吸收已可见暂停的完成增量),补偿 = no-rescore flush + reset + 合成 Pause/Resume 事件对(时刻取 edge 槽实测冻结点、恢复时刻 clamp 保 DB 单调),每次观察至多一对——消除"引擎不 reset、事件无记录"的全部路径。
  4. 前台自动暂停(foreground_pause.rs):启用门 = 窗口锚定(hwnd.is_some(),全屏/纯音频档案停摆);ForegroundGate 滞回(250ms 节拍 ×2 拍确认,fa1647aa SUSPECT 先例);自窗中性——经 GetAncestor(GA_ROOT) 进程比对排除主窗/浮窗/overlay/对话框,防"一点浮窗即误暂停";触发/解除均走 request(Foreground)。
  5. 契约 reason 化:live:paused/resumed 载荷 {"reason": kebab};DB pause/resume payload {"source"};LiveSessionStatus.paused_reason;live:media-paused/resumed 保留但前端零订阅(清理候选)。
  6. 前端每窗口单一数据源:CaptureStatusProvider(挂载查询含 paused_reason + 事件 + 5s 看门狗 + 守卫错误自愈),课堂页/浮窗/徽标/右栏收口到同一实例(浮窗为独立 webview,各窗一实例),删除全部二级监听与本地双状态。

备选方案

  • 方案 A(选定):来源状态机 + seq 补偿 + 前端单一数据源。优点:手动/自动语义机器化(根治 fa1647aa 类多写者漂移)、丢内容路径结构性消除、UI 不再依赖单点事件。代价:REQ-291 媒体暂停相关直写点全部收敛(frame worker 语义面改动,需真机走查)。
  • 方案 B:确认式跨线程回执(命令等捕获端点 Stop 回执再返回)。否决理由:命令置位 µs 级、端点停采 10ms 级,UI 无可感知收益;需跨线程回执通道与超时降级,复杂度不成比例。
  • 方案 C:仅补丁(暂停边沿跳过重打分 + 挂载补读 paused 字段)。否决理由:轮询间隙漏边沿、链式合并跨暂停、多窗状态分裂等结构性缺陷未除——不符合"优先彻底方案"的用户原则。

选择理由

  • 暂停是采集正确性的核心语义:延迟、丢内容、状态回退三条线共享同一根因(无来源布尔 + 轮询边沿 + 重打分阻塞 + 前端多写者),单点收敛收益最大;
  • 自动暂停(前台)与既有媒体随播随停共用同一状态机底座,未来任何新自动源(静音/失焦/可见性门控)只加一个条件源,不再触碰驱动端;
  • 前端单一数据源顺带修复"刷新后徽标空白"旧债,且为后续更多窗口(如后续收件箱/复习域页)提供同一模式先例。

影响

正面影响

  • 暂停生效确定性:点击后 UI 在 ≤1 个主循环节拍 + 无重打分(数十 ms~500ms)内翻转;
  • 快速暂停-恢复不再丢段/碎句/漏事件;DB 事件成对且时刻单调;
  • 徽标/浮窗/课堂卡 reason 三态文案,自动暂停原因可见;
  • 前台切走自动停、切回自动续(窗口锚定采集),手动暂停期间自动条件不越权。

负面/代价

  • REQ-291 媒体暂停链路面改动:auto_paused 局部标记删除、全部直写点收敛 request(Media),需真机样例走查(手动恢复后视频仍暂停→重锁存链路);
  • 事件载荷变化:旧前端监听忽略载荷即安全;旧后端(无此版本)对旧前端发空载荷 → UI 回落"已暂停"不崩;
  • 前台判定真机标定项:alt-tab 快速往返、全屏游戏抢前台、置顶弹窗;双屏并排场景(目标可见但前台在另一屏)会暂停——可见性门控留作可选项待用户裁决。

风险

  • 浮窗 always-on-top 点击被误判前台切换(最高风险)——自窗中性已覆盖,仍需真机验证;
  • media 恢复语义变更回归——fa1647aa 修复链路(800-807 兜底重锁存)已机器化并保留 worker 侧兜底;
  • 无窗口锚定采集的前台规则停摆属设计口径(用户已确认),不得静默启用。

合规性验证

  • cargo test --test app_lib_tests:2287 通过 / 0 失败 / 6 ignored(全量,隔离运行);
  • 前端:全量 vitest 597/597、tsc --noEmit 0 错误;
  • 新增核心文件实测(2026-09-09 审查纠偏,原 ≤300 口径失真):pause_state.rs 336 / live_session_pause.rs 353(审查修复轮 1 后)/ foreground_pause.rs 227——前两者超 300 已按 300-600 档登记 line-limit-exemptions.md;
  • 真机验收清单 8 条登记于 v0.20.7 交付记录(前台阈值标定待真机)。

相关决策

  • ADR-007(live-session 生命周期)、ADR-013(引擎预热与播放暂停驱动——本 ADR 修订其决策 2 的边沿 flush 语义与同标志/同事件表述)、ADR-030(ASR 质量批,flush/重打分参数化先例)
  • 需求:REQ-308/309/310(requirements-pool,2026-09-09 备注)
  • 设计:docs/archive/2026-09-09/2026-09-09-capture-pause-autopause-design.md([ ] 已归档 2026-09-09)