4.8 KiB
4.8 KiB
HEL-879 契约核准后 3 处实现时机收口(2026-10-27)
核对对象:hel-879-assembly @ 3228b1e0d7f822b7030f945e285eac74126771aa(总管《HEL-879-契约核准与接续.md》)。
本轮只修 3 处实现与已定契约 v2 不一致的问题:不改 v 信封字段、不改事件种类、不动 web/src、不重做已过绑定/备份/预算。
修前复现(全部先复现失败,与总管证据一致)
| # | 发现 | 修前证据 |
|---|---|---|
| 1 | open_session 只在 await create_dispatch 前 kick;建房真实让出事件循环后 cleaner 空转退出,注册后无人再观察截止时间 |
delayed-dispatch-before.json:{"remote_delete_count_after_ttl": 0, "expired_but_registered": true, "cleanup_owner_present": false}(与总管 delayed-dispatch.json 逐字段一致) |
| 2 | speak_proactive_row 旧写法 handle = await session.say(...);真实 SDK say 为同步函数、await handle=等整段播完 → 播完才发 proactive_start |
sdk-probe-before.json:proactive_before_playout_finishes.events=[],播完后才 ["proactive_start","proactive_end"] |
| 3 | _after_playout 先按 stream.text 发 final/end 再查回执;注入 text 非空、completed=False、receipt=None 的确切归属流 → turn_start→assistant_final→turn_end,与 v2「无完整同轮回执只 cancel」相反 |
同上:no_receipt_incomplete_actual_worker_events=["turn_start","assistant_final","turn_end"](实际 worker bind_voice_session 回调路径,真实 AgentSession/SpeechHandle) |
新增专项测试在未修复代码上先跑失败(repro-first):
tests/test_voice_rework_hel879.py::test_open_session_with_real_dispatch_yield_re_kicks_cleanup、
::test_second_registration_wakes_a_sleeping_cleaner_for_earlier_deadline → 修前 2 failed / 修后 2 passed。
修复(精确小修)
src/cybergril/voice/session.pyopen_session:注册成功并_persist_state()之后重新_kick_cleanup()——建房 await 期间旧 cleaner 可能已因「无可观察对象」退出,新截止时间需要新的所有者;自动截止、共享上限、重启补偿、有界放弃全部保留。测试用真正让出事件循环的建房替身(asyncio.sleep(0.02)),不再只用立即返回的 MockTransport 替身。src/cybergril/voice/worker.pyspeak_proactive_row:先取 handle(iscoroutine只解包 await 型替身——真实 SpeechHandle 自身可 await,不能误等)、立即发布proactive_start、注册该 handle 的proactive_end/proactive_cancel,再按需等待这一条播完(逐条顺序念保留)。src/cybergril/voice/worker.py_after_playout:assistant_final前置必要门——流completed且should_ack(合法同轮 receipt、非旧 epoch)才finish+confirm;未完整/无回执/旧 epoch 一律只turn_cancel(reason=incomplete/no-receipt),绝不先发 final 再决定不确认;φ/打断/失败/无归属 fail-closed 原样保留,完整+合法回执路径的 final 与 ack 均保留。
修后验证
- 本地:
delayed-dispatch-after.json:{"remote_delete_count_after_ttl": 1, "expired_but_registered": false, "cleanup_owner_present": false}(cleaner 在全部结清后按设计自行退出,无悬空协程)。 .12真 SDK(stdin 嵌入当前源码,零写入零网络,/opt/voice-agent/agent/venvlivekit-agents 1.8.8)sdk-probe-after.json:proactive_before_playout_finishes={"handle_done": false, "events": ["proactive_start"]}(播放中即可见 start);- 播完
["proactive_start","proactive_end"],字段v/kind/role/text/speech_id/message_id与 v2 完全一致; no_receipt_incomplete=["turn_start","turn_cancel"]、confirmed=[];- 完整+合法回执路径
["turn_start","assistant_final","turn_end"]+ ack 保留。
- 真SDK回归并入常驻检查
scripts/gen_hel877_sdk_check.py(14/14 ALL PASS,../sdk-check-hel879-timing.log):新增「主动播报时机」「主动播报打断只有 cancel」「无回执不 final」三负例,均驱动实际 worker 模块注册的真实回调。 - 产品套件:pytest 352/352(3 次连跑);ruff(src/tests/tools) 0 错;
verify_hel877_voice_chain.py8/8;需求检查 4/4(origin/main 与c121c42各带/不带 handoff-HEL-879.json)。字幕样例subtitle-samples/与 v2 契约文档无需再生成(字段与生命周期未变)。
边界
零真实 LLM/嵌入/ASR/TTS/麦克风;.12 仅 SSH stdin 执行只读检查(零写入零网络,stderr 仅既有 jwt 弱密钥告警);零下载零安装;未部署、未合并;PR#11 与 hel-870-assembly 未动;权威账/冻结/共享服务/队员配置未碰;真实 cap 仍 0。R5 真机、完整 CG-011、R6/R7 继续承接,无需求变更(v1.11/60 项)。