# 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,通过;保留旧行的未知节点为空,不回填默认节点,重复运行按一次性迁移约定失败且不改原值。 验证命令(对应仓库根目录): ```bash node tests/cockpit-video/run.mjs --hosts node tests/cockpit-video/run.mjs node --test 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 交接](<../.scratch/cockpit-video-monitoring/task20-frontend-result.md>)。初始夹具/编译错误与行为红信号已区分,不将缺依赖、导入冲突、旧 class 命中算作产品红灯。 ## 从第一性原理保留的约束 1. **UI 可见性不是观看归属。** [Panel](<../src/modules/cockpit/components/VideoMonitorPanel.vue>)同步观察真实选择/一级模式离开;同轮回来也不能复活旧观看。[Store](<../src/modules/cockpit/stores/videoStore.js>)只允许仍有效的来源重绑同码;合法重绑不建第二条流。刷新/锚点暂隐/部分失败保留选择,完整新快照确认移除才关闭。 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,仍未关闭) `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 也不是规格的最后一张票。