# 阶段4A独立实施复核 日期:2026-09-23。复核者:用户指定外部AI(ZCode会话)。范围:4A实施(Owner只读接口、PlayerKVLocalReadResult、PlayerKVGameSessionBridge、KVGameBridgeTests、三个csproj单行差异)的只读代码审查+证据核验+独立复跑。本轮未修改任何代码/csproj/共享文档,未访问正式服,未使用子Agent。 ## 受审版本(SHA256,本复核逐项重算与交付一致) | 文件 | SHA256(前16) | |---|---| | PlayerKVSessionOwner.cs | `BFB5AC0DF38BCACC…`(完整值与复核提示词一致) | | PlayerKVLocalReadResult.cs | `9A00AA0ECEA286FC…` | | PlayerKVGameSessionBridge.cs | `DD888351BFA4BBA0…` | | 三个旧csproj | 与`project-diff-check.json`一致;本复核用`KVGameBridgeTests/*.csproj.baseline`逐文件diff,**各恰好只有已审一行Compile新增** | 边界证明核验:`owner-diff-check.json`——移除新增读取接口后Owner还原为3B终版`F2F0430B…`(Owner其余部分字节级未动,票据/关闭/保守语义未改的强证据);`scope-hash-check.json`——其余基线生产文件、3C测试Program、3B/Review Program未变。 ## 复核结论 **接受本轮4A实施。无P1/P2。** 建议主会话逐条裁决后关闭4A;4B仍需独立方案与评审,本报告不自动授权。 ## 代码审查(静态推导+实测结合) **ReadLocalSnapshot(Owner行141–167):通过。** 操作租约内执行`state.ReadLocal()`(3A原子合并读);读后在admission锁内复查`IsCapturedOwnerCurrent/closing/closed`,失效优先返回`StaleOrClosing`(B-S3优先级落实);`InvalidOperationException`(非Ready)同样先判失效再归`NotReady`;失败结果Data=null,**无伪空档**(A1/A3实测)。租约计入Close drain(A8的read-close-reentry用例证实)。 **PlayerKVLocalReadResult:通过。** 成功路径深拷贝为ReadOnlyDictionary(不可变、与内部字典脱钩——A1断言旧快照不随后续写变化);失败路径不携带数据。 **PlayerKVGameSessionBridge:通过。** 逐项: - 尾链串行(tail任务链):Open/Close全部串行化,锁内只做短临界区,**任何await不持桥monitor**。 - Close屏障:`CloseAsync`锁内`closeGeneration++`+撤销活动槽,**锁外**`Cancel()`(取消回调不持monitor);`CloseCoreAsync`等待前驱后关闭Owner,完成后才放行后续Open——旧传输/Owner真正收尾前新Open不发布句柄(A4实测:忽略取消闸门下Close与102 Open均未提前完成,释放后MaxActive=1)。 - 排队Open取消:登记回调仅在`!Entered`时置Cancelled;进入后在临界区检查token/generation——无窗口(A7实测:取消立即返回,原Close与后续合法Open正常完成)。 - 重复Open:后者经尾链先`CloseSlotAsync(previous)`再建新槽,至多一个活动Owner,旧句柄因`Revoke`全部拒绝(A5/A6实测:旧句柄Update/Delete/Flush/Read/Wait拒绝且102请求数不变)。 - 初始Get失败(B-S1):`opened.Success==false`→清活动位+Revoke+DisposeLifetime→OwnerRejected,后续Open可用(A3实测initial-read-failure-releases-slot)。 - 清理失败:catch路径`closeFailed=true`且**保留**Creation/Closing/Owner,后续Open一律CloseFailed(A3实测close/create-cleanup-failure-blocks-reopen);Close失败同置closeFailed,重复Close返回同一lastClose任务。 - 取消与CTS.Dispose并发:`Slot.Cancel/DisposeLifetime`经`cancellationGate`串行,Cancel异常被记录并上抛为清理失败(保守)。 - 固定句柄不重绑定:Handle只持创建时Slot;不Dispose登录session(仅ReferenceEquals归属检查)——session提前Dispose或重登换Cookie后读写/等待拒绝、桥接仍能完成Close且零变更外发(A9实测)。 - 不隐式Flush、不重试Unknown:Close路径只调Owner.CloseAsync(3B语义),A10实测关闭0写、重开读到pending、显式Flush确认;A3-write-unknown-retains-ticket-semantics实测Unknown票据语义保留。 **非阻塞观察(P3)** 1. **P3-1|KVSyncTests偶发一次失败(预存在,非4A引入)。** 本复核首轮批量复跑出现"Passed 85; Failed 1"(当时输出管道未捕获失败用例名);随后**连续3次完整复跑86/86、退出码0**。该套件编译的5个生产文件hash未变(scope-hash-check+本复核重算),判定为偶发时序敏感(该套件含子进程文件锁用例)。建议:后续证据记录保留失败用例名;若复发,单独立项排查偶发源。不阻塞4A。 2. **P3-2|Handle各操作的失效判定粒度略异。** Update/Delete/Read用`slot.Revoked`,WaitForTicket用完整`slot.IsCurrent(Epoch)`(含session/Cookie/外部epoch检查)——后者更严,方向保守,无安全影响;仅备忘统一时机(可留4B接线前评估)。 3. **P3-3|`isCurrent`回调需轻量。** `Slot.IsCurrent`同步调用外部回调;限定自审已记录"不可同步等待自己的Close",与设计一致,向4B接线方转述该约束即可。 ## 独立复跑证据(本会话自有) | 命令 | 结果 | 真实退出码 | |---|---|---| | `dotnet run --no-restore --project KVGameBridgeTests` | 18组通过,断言226,失败0(新run `20260923081335_85da5c67…`) | 0 | | KVContractTests | 42/42 | 0 | | KVSyncTests | 86/86(见P3-1:另有一次偶发85/86) | 0 | | KVSyncCoordinatorTests | 29/29 | 0 | | KVSyncCoordinatorReviewTests | 通过 | 0 | | KVStage3CTests --offline | 8/8 | 0 | | `dotnet build` KVLiveTests(仅构建未运行) | 0错误 | 0 | | `uloop compile --force-recompile true`+get-logs | 错误0条(证据:`KVGameBridgeTests/unity-compile-review.json`、`unity-errors-review.json`) | 0 | 脱敏核验:`artifact-and-transport-check.json`——result.json中fixture key/value、Cookie/Token字段、用户路径0命中,accessedFormalServer=false;传输边界依据为synthetic.invalid替身注入+默认handler仅反射检查(交付已如实声明该字段非抓包结果)。 ## A1–A12逐行核对 交付矩阵§4的18个用例与本复核复跑输出、源码路径逐项对应属实(A1合并快照/A2校验取消与Get中Close/A3失败释放槽位与清理失败阻塞/A4-A7关闭排水与排队取消/A8读竞争与优先级/A9会话失效/A10不隐式Flush/A11默认传输/A12回归与边界);未发现矩阵与证据不符项。 ## 结论 **接受本轮4A实施;无P1/P2;P3三条仅记录。** 建议主会话逐条裁决P3后关闭4A;4B启动需独立方案与评审(含新档策略、游戏回调隔离、消费流程),不因4A通过自动放行。