cockpit-video-lifecycle-acceptance.md 18 KB

03 视频生命周期续作验收记录(2026-11-12)

当前结论

本轮修复已验证并独立提交,03 仍保持打开。前端不再以手工拼装的地下宿主测试代替 Root 旅程;主服务不再用参数集合模拟 SQL 查询;提供方取消由独立持久化快照和 CAS 保护。下方历史审查中的几个核心缺陷已修,但重启后的停止回执恢复和超出 SIP 订阅期限的迟到应答仍是代码收口缺口,不能仅以测试全绿关闭。

固定续作基点:前端 dcbe5691、后端 cb435cd4。前端期间他人提交 0f3db501(资产详情)保持原样;本轮没有用 HEAD 重建共享文件、没有覆盖或暂存其它在制品。

本轮提交

仓库 提交 内容
前端 ac5419cd 真实 Root 三入口、刷新和同轮退出/失效来源保护
后端 3969e499 CAS 取消意图、完整代次流名、原节点回收、旧 GB 对话接管、SIP/RTP/Hook 精确边界与测试
后端 719fe092 ACTIVE 写入/提交失败补偿、旧新观看共享/加入期限、实际媒体节点字段、H2 SQL 回归

前端作者 ChenJZ;后端作者 Kazerin。提交前核对身份;均未推送。本记录不把其它会话的提交或在制改动计为本票成果。

实际验证

  • 前端:43 项通过——真实 Root hosts 9、辅助 journey 16、session/transport/player/contract 18。9 是用例数量,不是票面九项全部完成。
  • 后端:本次 mvn -o -fae ... clean test,未使用 failure.ignore,真实退出码 0、BUILD SUCCESS。模块汇总为 zksy-system 270、zksy-admin 844、zk-api-service 22、video-service 92,共 1228 项,0 失败/错误/跳过。clean 排除了历史编译产物命中;不把旧报告混进计数。
  • 其中 实际 SQL/事务测试 23 项,不是 Map 模拟计数;使用真实 MyBatis-Plus SQL、H2 内存数据库、Spring JDBC 事务管理器。媒体与提交失败/确认丢失是受控外部故障。
  • 提供方包括 CAS/期限竞态 7 项、旧 GB 接管贯通 7 项、受管 GB 深层组合 18 项,以及节点/Manual/Hook/Invite 保留等用例。底层 I/O 是替身,不代表实际设备已停止。
  • 实际节点迁移测试 在 PGlite 内存库执行真实 SQL,通过;保留旧行的未知节点为空,不回填默认节点,重复运行按一次性迁移约定失败且不改原值。

验证命令(对应仓库根目录):

node tests/cockpit-video/run.mjs --hosts
node tests/cockpit-video/run.mjs
node --test tests/cockpit-video/reconnect.mjs tests/cockpit-video/session.mjs tests/cockpit-video/transport.mjs tests/cockpit-video/player.mjs tests/cockpit-video/contract.mjs
# 后端使用本机既有离线settings:
JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 mvn -o -fae -gs .scratch/log-gis-contract-20261111/maven-offline-settings.xml clean test

本轮完整后端日志;前端隔离基点与 RED/GREEN 交接。初始夹具/编译错误与行为红信号已区分,不将缺依赖、导入冲突、旧 class 命中算作产品红灯。

从第一性原理保留的约束

  1. UI 可见性不是观看归属。 Panel同步观察真实选择/一级模式离开;同轮回来也不能复活旧观看。Store只允许仍有效的来源重绑同码;合法重绑不建第二条流。刷新/锚点暂隐/部分失败保留选择,完整新快照确认移除才关闭。
  2. 收到或发送请求不是资源证明。 提供方协调先登记开始意图,再调用媒体;取消不可被独立迟到快照覆盖,重复 start 不重复分配。启动期限固定,超期媒体结果进入清理,不对外发布。取消时启动尚在途,即使先收到删除确认也不能提前报 STOPPED。
  3. 真实句柄与节点不能猜。 每代使用完整 UUID;新受管 GB 不再复用传统通道流名。Manual 适配只保存实际 add 返回的 key,GB 适配在已记录节点探测。媒体查询错误不等同“不存在”,构造播放 URL 不等同“流就绪”。
  4. 旧流接管必须能精确退出。 接管旧 GB 的实际节点、Call-ID、SSRC、分配 token、SIP tags 后,记录不可复用的观察凭据;只有匹配 BYE 2xx 与本次 RTP 关闭证据都成立才完成。同名新对话/新 allocation 不复用旧关闭回执。旧 Manual 后来观看者复用亲自确认的 key/node,不再 add;冷进程停止可接受 SQL 保存的明确 node+key,而非默认节点。
  5. 提交失败不等于媒体没启动。 主生命周期对 ACTIVE SQL 或 commit/ack 失败先重新读取同代记录与其他观看者,再只释放自己或回收最后一位底流。数据库持续不可用时保留已提交的意图供扫描,不盲停。
  6. 物理代次启动期限与后来观看者的期限不同。 已 ACTIVE 的共享底流可以被后来观看者加入;不能因为原 30 秒期限已过便立刻清掉新加入者。加入者自身仍受有界失联期限约束,不能靠其他人心跳永久保活。

独立双轴审查及后续修复

Standards 与 Spec 在不同子代理中独立运行,未用 Lead 的自查替代。原始结论保留在本会话:

  • Standards:0 成文硬违规;确认两个工程风险——旧 GB 可接管但 stopManaged 拒绝旧名,以及第二名旧 Manual 观看无法复用。未把类体量、合理适配转发、状态字符串一概判成硬违规。
  • Spec:确认上述两项,加上共享加入者的期限竞态、旧 Manual 停止遗漏已持久化节点,共四项实际错误;同时保留 SIP 迟到/重启证据缺失的未完成结论。
  • 四项已补代码与回归:主期限/节点/有界加入/旧接管的 RED→GREEN 日志在后端 .scratch/video03-lifecycle/review-shared-*;旧 GB 回收已补真实 adapter→Play→RTP/Hook/Invite/Session/Commander 组合,不再只 mock stop=true。整体 clean test 已覆盖最终拼接状态。审查修复后未声称“独立审查零缺口”。

第三轮:SIP 关联、终拒与资源归还(2026-11-13)

第三轮针对前述“超期应答/跨重启回执”边界继续实施,新增两个后端提交(均未推送):

提交 内容
a58b01ee SIP 发送前持久绑定与超期应答恢复;outgoing/peer 终止证据分开;原快照 CAS 删除;VideoStopReceipts 持久分配与停止证据;SSRC token 化原子归还未命名复用错位;legacy 兼容收口
1c0609b9 INVITE 明确终拒单独存证;离线后凭原 dialog 补发精确 BYE

每项都先复现失败:异步关闭回执后池号未归还(期望 100 实际 99)、设备协商 SSRC 后关闭被拒、终拒后 RTP 已关仍永久 Pending。修复后 完整后端回归 为 291+851+22+158=1322 项,0 失败/错误/跳过,BUILD SUCCESS;未使用 failure.ignore。前端 43 项本轮未改动,前轮结果仍有效。

本轮仍需保留为未完成的事实: 真实 Redis/Cluster 拓扑与同槽、真实 SIP 设备与 ZLM 物理停止、真实数据库与迁移执行均未验证;Manual 创建响应连实际 key 一起丢失时仍按批准方案 Pending。1322 项是离线合同证据,不能当作现场播放或部署验收;03 不关闭。

最后补充:代理回执类型校验

最终核对又发现两类不够保守的响应处理,均补了实际红→绿:非字符串 data.key 不再转换成代理句柄;删除响应必须有布尔 data.flag=true,仅 code=0、缺 flag 或字符串 "true" 不能证明删除。正常成功夹具同步为真实 API 的明确确认形状,未删除原断言。后端追加提交 cf823ede。

这四个新增坏响应参数用例后,再次完整 mvn -o -fae ... test(无 failure.ignore)通过:270 + 844 + 22 + 96 = 1232 项,0 失败/错误/跳过。前面的 1228 项 clean test 是修复前一阶段的真实记录,不覆盖历史。最终日志见完整确认回归。

票面九项验收证据映射(2026-11-13)

逐项对照 .scratch/cockpit-video-monitoring/issues/03-三入口统一与观看资源生命周期.md 第 17–25 行:

# 票面要求 结论 可执行证据
1 三宿主同一入口/唯一面板 完成 hosts.mjs 9 项:浮动/无位置固定/地下均经真实 Root→设备详情→最新指标 TAB→监控,断言全局面板唯一
2 刷新、锚点暂隐不重建 完成 hosts.mjs:263 三宿主刷新后同一 <video> 节点,prepare=1、stop=0
3 关闭/离开/切模式/选择失效/离页/隐藏清理并手动恢复 完成 hosts.mjs:222(切模式同轮往返)、journey.mjs:251(visibilitychange)、journey.mjs:162(Escape)、journey.mjs:85(选择失效)
4 统一模式切换旧流结束接缝,供历史/云台票消费 部分 仅覆盖 managed live(hosts.mjs:222、RtpServerLifecycle 测试);PLAYBACK 仍走 legacy 无代次路径。票面第 38 行明示“未完成的历史/云台功能不作为本票关闭前置”,故不阻断本票
5 新设备不被旧请求覆盖;取消后迟到响应释放 完成 session.mjs 6 项:并发 open、同轮关闭、STARTING 轮询、取消未确认、迟到响应隔离、prepare 归属
6 媒体已启动但登记失败有补偿;不以未收到 sessionId 推断无资源 完成 VideoPreparedSqlTransactionTest 23 项(真实 MyBatis/H2):ACTIVE 落库失败/提交失败/ack 丢失补偿;bd80b6a4 补 CREATING→OPEN 回执失败时关闭已建端口
7 多匿名+既有用户共存,最后一位才回收共享底流 完成 VideoPreparedSqlTransactionTest 混合观看用例(两种退出顺序);VideoLiveSharingTest 共享回收决策
8 归属/有效性明确,错误归属不能操作他人资源,清理失败如实等待 完成 journey.mjs:115(另一会话的 HTTP 成功不能确认当前清理)、journey.mjs:333(清理失败阻断新请求)、后端 isSipTerminated/CLEANUP_PENDING 语义
9 三宿主+对抗旅程回归,只记实际完成的合同验证 完成(离线) 前端 43 项 + 后端 1326 项;票面明示物理停止由 06/07 覆盖

九项中 8 项完成、第 4 项部分(且按票面切片边界不阻断);物理停止与真实部署不在本票验收范围。

第六轮:契约定案与回执保留期(2026-11-13,仍未关闭)

a5a0a707:

  • 契约定案:终止状态只能在同时具备 allocationClosed 正面证据时被声称。真实适配器(GB/Manual)确实在该字段里表达“已验证拆除”;旧用例的替身只返回裸 STOPPED,已修正为与真实契约一致,而不是放宽生产判定。
  • 回执保留期:VideoStopReceipts 在同一次已提交事务内,对终态快照使用 SET ... EX(7 天);未确认记录仍不设 TTL,重启后必须可读。新增断言验证“终态才设置、未结不设置”。

完整后端回归:291+851+22+162 = 1326 项,0 失败/错误/跳过,BUILD SUCCESS(round10-contract-ttl-reactor.log)。

仍未解决:

  1. SIP_RECOVERY_V1:*(SipInviteSessionManager)仍无 TTL/清理——本次只处理了 VideoStopReceipts;需要先确定 pending 意图的安全过期策略(删除过早会丢失迟到应答的恢复依据)。
  2. SIPCommander:601 legacy(非 managed/adopted)路径仍调用非原子 removeByCallId。这是旧兼容路径的既有行为,改成 CAS 会改变旧路径语义,超出本票必要范围,未动。
  3. 规模性问题(内联全限定类名、managed_ 判定散落、stopManaged 过长)仅记录,未重构。

第五轮:双轴审查确定缺陷修复(2026-11-13,仍未关闭)

独立双轴审查指出的三处确定缺陷,已修复并各自补回归(均先在测试中失败,再修复):

提交 缺陷
2b7ca8e0 终拒收敛实际是死代码:refused 在 expected == null(终拒时必然如此)下恒为 false,守卫第二项恒真,即使 RTP 已关也永久 Pending;且终拒证据写盘晚于 errorEvent。现改为直接查终拒证据、并把写盘提到回调之前;新增“486 + 真实 RTP 已关 ⇒ stopManaged()==true”端到端用例
b8c57b35 peer BYE 证据写失败即跳过 RTP 回收:对端已 BYE、媒体仍开。现改为先收口媒体,仅在证据未持久时保留 Invite/dialog(不冒充 SIP 终止确认);新增回归
bd80b6a4(前批) ZLM 已创建端口但回执 CAS 失败时静默泄漏;同名新分配被直接删除

最新完整后端回归:291+851+22+161 = 1325 项,0 失败/错误/跳过,BUILD SUCCESS(round9-peer-reclaim-reactor.log)。

仍未解决(不掩盖):

  1. VideoLifecycleService 的 STOPPED 与 allocationClosed 契约 → 已在 a5a0a707 定案(见第六轮)。
  2. 审查指出的无界 Redis 键增长 → VideoStopReceipts 已在 a5a0a707 为终态设置 7 天保留期;SIP_RECOVERY_V1:* 仍未处理(见第六轮)。
  3. SipInviteSessionManager.removeByCallId 仍是先读后删的非原子旧实现,SIPCommander 仍在调用它,与双索引原快照 CAS 目标矛盾。
  4. 规模性问题(内联全限定类名 59 处、managed_ 前缀判定散落 28 处、stopManaged 单方法过长)只作判断题记录,未重构。

第四轮:规范审查修复(2026-11-13,仍未关闭)

独立 Standards 复核指出的确定问题已修复并提交(未推送):

提交 内容
933317f5 协商 SSRC 后由 token 归属真实归还池号;归还失败绝不记为已归还
4e3cc34f 终拒只接受最终失败状态码(1xx/2xx/401/407 不算)
cf05cfda 移除未使用的协商 SSRC 重载
14000384 失败响应按 callId+cseq 移除 SIP 订阅,避免残留订阅二次触发错误
bd80b6a4 ZLM 已创建端口但回执 CAS 失败时关闭该端口并回调,不再静默泄漏;managed/adopted 收尾改用原 Invite 快照 CAS 删除;移除死代码字段;终拒写入失败记日志

仍未修复的两项(不掩盖):

  1. VideoLifecycleService 的取消收敛语义:审查认为缺少 allocationClosed 时不应置 STOPPED,但简单收紧会使适配器真实返回的 STOPPED 永久停在 Pending。我两次尝试都破坏了既有用例,已回退,未提交半成品。需要先确定契约(STOPPED 是否本身即终止证据),再动代码。
  2. peer BYE 持久化失败时跳过 RTP 回收:属真实残留缺陷,我未找到同时满足“保留 pending 证据”与“仍回收媒体”的最小改法,已回退。

范围问题(我的判断失误):为修本票资源归还,把 getPlaySsrc/getPlayBackSsrc 全量 token 化,触及旧管理端点播/回放路径,与 spec “不改旧接口权限”边界冲突。PLAYBACK 仍走 legacy 无代次路径,因此“统一模式切换接缝供历史票消费”只覆盖 managed live。

最新完整后端回归:291+851+22+159 = 1323 项,0 失败/错误/跳过,BUILD SUCCESS。前端 43 项未变。

依赖、迁移与剩余工作

用户明确批准 H2 2.1.214 仅 test scope(发布许可 MPL-2.0/EPL-1.0);jar SHA-256 d623cdc0f61d218cf549a8d09f1c391ff91096116b22e2475475fce4fbe72bd0。没有把 H2 放进应用运行依赖,没有连接真实数据库。

真实部署前按顺序核对 01 匿名归属迁移、03 准备会话迁移、03 实际节点补充迁移。均未在真实数据库执行。

03 仍未关闭的具体原因:

  • 上轮列出的“超期 SIP 200 无恢复记录”和“停止回执仅在进程内”已在 a58b01ee/1c0609b9 修复:发送前持久绑定、退出重建后仍可按原对话 ACK 并终止,RTP/BYE 正面证据持久化且不跨代复用。这些已不再是未完成项。
  • 仍未验证的是实际部署条件:真实 Redis/Cluster 拓扑(多 key Lua 的同槽前提)、真实 SIP 设备与 ZLM 的物理停止、真实数据库上的三条迁移与多实例并发。离线替身测试不能替代这些事实。
  • Manual 创建响应连实际 key 一起丢失时没有已核实的恢复接口,按用户批准保留 Pending,不猜 key、不借 close_streams 伪称删除代理。正常有 key 的停止/共享路径已经测试,但此异常不能写成“已回收”。

本轮没有启动服务、浏览器、真实媒体接口、真实 SQL 或设备操作;按项目规则已跳过应用 build/debug。43/1322 是离线合同证据,不是现场播放、部署可用性或物理云台停止验收。04–07 未在本轮实施,03 也不是规格的最后一张票。