## Parent [#85](https://github.com/wt159/SoundBridge/issues/85) — [Spec] 播放器状态与公开 API 契约实现计划。 ## What to build **任何错误下,调用方都先收到原因(`onError`)、再看到状态变化**(如果确有状态变化);autoSkip 跳轨成功时可以只有 `onError`、没有状态事件;skip 失败时补一次状态事件且遵守「无相邻重复」。现状播放列表错误路径是先 state→error→skip,解码路径已经是 error→state,两者不一致。 - 调整播放列表错误路径的上报顺序;停机期静默与 `index < 0` 批量汇总的既有行为不变。 - 用回调序列记录器把 error→state 写成判据;补一条 autoSkip 跳轨成功「不产生撒谎状态」的场景。 - 不新增错误码、不改 `onError` 签名、不改状态集合。 ## Acceptance criteria - [ ] 播放列表错误与解码错误两条路径的顺序一致,均为 error 在 state 之前 - [ ] autoSkip 跳轨成功时状态序列不出现多余的中间态;skip 失败有且仅有一次状态事件 - [ ] 状态序列无相邻重复;坏输入 55 例与错误相关既有用例全绿 ## Blocked by [#86](https://github.com/wt159/SoundBridge/issues/86)(GitHub 原生 blocked_by 已连接)。. **必须遵守(来自 spec、ADR 0002/0003/0004 与 `doc/bad-input-spec.md`):** - 七个控制入口保持 `void`;不新增错误码、不改 `onError` 签名、不新增状态机、不扩状态集合(对外仍 `Playing/Paused/Stopped` 三态)。 - 判据**不新造数值**:沿用 `max(1%,150ms)`、`r × good`、5pp、零容差差分、getter 与回调 ≤50ms、状态序列无相邻重复、每曲一次 duration。 - 坏输入 55 例 / 396 断言与覆盖度矩阵 15 行**一字不动**;14 个 `sdk_*` ctest 目标、TIMEOUT、`-j4`、快集 / 全量集划分全部不动。 - 新增用例若写文件必须带 pid(`-j4` 撞名假红的教训);线程契约用 `promise/future` 或轮询验证,不裸 sleep。 - 事实基线见 [#76](https://github.com/wt159/SoundBridge/issues/76) 的 `research/public-api-contract-facts` 分支;术语见 `GLOSSARY.md`。
Activity
fix: 错误路径统一 error→state,状态通知去重(#87 / T02)
wt159 commentedon Nov 8, 2026
Resolution(2026-11-08,图外实现 effort #85 / PR #93)
onMusicPlayListError统一为先onError、后状态,与解码错误路径一致。经 code-review 修正后,状态事件只在「不跳轨 / 跳轨失败」时发:autoSkip 成功时状态可能根本不变,切歌前后都是 Playing、不闪 Stopped;skip 失败补且仅补一次。updatePlayState的去重键是「最近真正对外发出的状态」而非m_state初值 —— 初值 Stopped 从未发过,首播即失败 / skip 失败时仍必须通知一次;同时落实「重复控制命令不重复发 onStateChanged / 状态序列无相邻重复」。判据:
play without track reports error without a state event(错误不进状态面)。已知缺口:该用例走_play()的cur==nullptr分支,不经过onMusicPlayListError;C1 的 error→state 顺序缺端到端判据,因为现有接缝无法稳定注入「带状态变化的错误」,而新增可注入接缝违反规格 §8「不新造接缝」。由生产链改动 + 代码审查 + 全量回归锁定,详见规格 §10.4。