# 阶段4B1独立实施复核 日期:2026-09-23。复核者:用户指定外部AI(ZCode会话)。范围:4B1实施(`PlayerKVLoginContext.cs`+`PlayerKVLoginPrepareResult.cs`+`KVLoginContextTests`)的只读代码审查+证据核验+独立复跑。本轮未修改任何生产/测试代码、csproj、共享文档或原报告;未使用子Agent;未访问正式服;未运行KVLiveTests或3C --live-s101。 ## 受审版本(SHA256,本复核逐项重算) | 文件 | SHA256 | |---|---| | PlayerKVLoginContext.cs | `7552C331BCD7B5B51A9726085D25319E9D77653E5519EE76D7772EEB4B757A29`(与提示词一致) | | PlayerKVLoginPrepareResult.cs | `108CA361C7153D19A08CD348B8325538019A86C426BDD80B907528399F22B290`(与提示词一致) | `scope-hash-check.json`68项既有源码/meta/测试Program/csproj——本复核重算**全部unchanged**(UserService/PlayFabMgr/DataCenter/Stages/4A/3A/3B/账户源码零改动)。历史失败run(21组/1失败)与中间run均保留未覆盖;最终run `20260923084050…`23组/274断言/0失败。 ## 复核结论 **接受4B1实施。无P1/P2。** 建议主会话逐条裁决后关闭4B1;4B2/4B3仍需各自独立方案与评审,本报告不自动授权。 ## 代码审查(静态推导+实测) **PlayerKVLoginContext:通过。** 逐项: 1. **单次Prepare/首token(P3-A/P3-C裁决落实)**:`preparation`单TCS;重复调用返回**原任务引用**;不同token仅`differentTokenCount`诊断+Debug.WriteLine,不替换生命期、不Assert不抛(行75–94)。Close后再次Prepare仍返回历史任务(可能是历史Prepared成功),符合P3-A裁决的"历史Task语义";仅Close先于首次Prepare时返回Cancelled(行92)。 2. **workSettled与对外结果分离(§4.3)**:`PrepareCoreAsync`的finally**无条件**先完成workSettled(行173),随后才走`CompleteFailureAsync`→await CloseAsync→发布失败结果(行214–226);CloseCoreAsync只await workSettled与Bridge.Close,**从不等待对外Prepare任务**——互等环在结构上不存在(B1-10实测无循环等待)。 3. **Close语义**:锁内`revoked=1`+创建closing TCS(返回Task前同步撤销✓);重复Close同任务;close-before-Prepare时workSettled立即完成(行237)→后续Prepare零请求Cancelled(B1-11实测)。Close经`Task.Run`执行,取消注册回调不内联执行清理(行239–243注释及实现);faulted任务经ContinueWith观察,无未观察异常。 4. **资源次序(§4.4)**:`lifetime.Cancel→await workSettled→await bridge.CloseAsync→session.Dispose→registration.Dispose→lifetime.Dispose`;**Bridge Close失败时session不Dispose**、引用与faulted任务保留(行255–257注释及实现,B1-12实测);`lifetime.Cancel`自身异常延迟上抛为CloseFailed。 5. **认证→Get→快照顺序**:session工厂返回后校验`ApiBaseUri==api`;登录→epoch复查→Bridge.OpenAsync(同一session、固定根/epoch、`EpochCurrent`委托、context CTS)→`ReadLocalSnapshot`→发布前锁内复查revoked(行149–161)。Stale/NotReady区分保留,SyncStatus证据保留。 6. **外部委托**:`EpochCurrent`在无context锁状态下调用外部回调(行191–201);异常记录类型并自动触发CloseAsync→Unexpected;同号重登靠Cookie引用失配判定(IsCurrent的ReferenceEquals)。 7. **IsCurrent**:未Prepared恒false;Close后revoked=1恒false;历史快照仍在结果对象中可枚举但不授权(P3-A)——B1-11-P3A专项用例实测。 8. **无越界**:文件无Unity/PlayFab/GContext/业务初始化/写接口引用;公开构造固定`DefaultApiUrl`;internal工厂不外泄session/handle/传输。 **PlayerKVLoginPrepareResult:通过。** 成功路径对快照再深拷贝为ReadOnlyDictionary(与4A快照双保险脱钩);失败Data=null;`OriginalError`/`CleanupExceptionType`/各阶段可空错误字段完整;XML注释落实P3-B(失败/取消等待真实收尾、忽略取消可长期挂起、以CloseAsync为准)。 **P3-A/B/C裁决落实核对:全部落实**(A=历史任务语义+B1-11-P3A用例;B=两处XML注释;C=诊断计数+Debug.WriteLine,无Assert)。 ## 独立复跑证据(本会话自有,真实退出码) | 命令 | 结果 | 退出码 | |---|---|---| | KVLoginContextTests | 23组/278断言/0失败(新run `20260923090458_327462ec…`) | 0 | | KVGameBridgeTests(4A) | 18组/226断言/0失败 | 0 | | KVContractTests(阶段2) | 42/42 | 0 | | KVSyncTests(3A) | 86/86 | 0 | | KVSyncCoordinatorTests(3B) | 29/29 | 0 | | KVSyncCoordinatorReviewTests | 通过 | 0 | | KVStage3CTests --offline | 8/8 | 0 | | KVLiveTests仅build | 0错误 | 0 | | uloop compile --force-recompile + get-logs | 错误0条(证据:`KVLoginContextTests/unity-compile-review.json`/`unity-errors-review.json`) | 0 | 本复跑未再现KVSyncTests偶发失败(86/86,退出码0);按4A关闭裁决口径,P3-1根因仍为未定,不以本轮通过宣称已消失。 ## B1-01~B1-16逐行核对 交付矩阵§4的23个用例与最终run的test名/断言数逐项对应(含B1-04拆三组、B1-09拆两组、B1-11含P3-A专项与49断言并发组、B1-12拆三组、B1-13拆两组);未发现矩阵与证据不符。"通过"均为合成替身下的技术验证,与交付口径一致。 ## 非阻塞观察(P3,仅记录) 1. **P3-1'|`IsCurrent`对`session`的非阻塞读取依赖prepared发布顺序。** `Volatile.Write(prepared)`与字段写入的顺序经volatile读语义保障正确(acquire),实现无缺陷;仅提示后续若增字段需维持"先写字段后写prepared"约定。位置:PlayerKVLoginContext.cs行54–63/152–159。 2. **P3-2'|`CompleteFailureAsync`在CloseFailed时仍保留原始错误。** 已按合同实现(OriginalError);建议4B2接线文档显式引导调用方同时检查`CleanupFailed`与`OriginalError`,避免只看Error==CloseFailed丢失根因。文档级建议。 3. **P3-3'|首次失败run的夹具缺陷记录。** 首轮21/1失败系测试夹具token注册错误(自审已记录),非生产缺陷;历史run保留正确。无行动项。 ## 结论 **接受4B1实施;无P1/P2;P3三条仅记录。** 建议主会话逐条裁决后关闭4B1。4B2(游戏加载与内存隔离)需独立完整方案(业务档校验、空档阻塞策略、初始化副作用、OnInitData时序、401/退出接线)与评审后实施;4B1关闭不放行游戏接线或正式业务KV。