6.4 KiB
阶段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.json68项既有源码/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:通过。 逐项:
- 单次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)。 - workSettled与对外结果分离(§4.3):
PrepareCoreAsync的finally无条件先完成workSettled(行173),随后才走CompleteFailureAsync→await CloseAsync→发布失败结果(行214–226);CloseCoreAsync只await workSettled与Bridge.Close,从不等待对外Prepare任务——互等环在结构上不存在(B1-10实测无循环等待)。 - 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):
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。 - 认证→Get→快照顺序:session工厂返回后校验
ApiBaseUri==api;登录→epoch复查→Bridge.OpenAsync(同一session、固定根/epoch、EpochCurrent委托、context CTS)→ReadLocalSnapshot→发布前锁内复查revoked(行149–161)。Stale/NotReady区分保留,SyncStatus证据保留。 - 外部委托:
EpochCurrent在无context锁状态下调用外部回调(行191–201);异常记录类型并自动触发CloseAsync→Unexpected;同号重登靠Cookie引用失配判定(IsCurrent的ReferenceEquals)。 - IsCurrent:未Prepared恒false;Close后revoked=1恒false;历史快照仍在结果对象中可枚举但不授权(P3-A)——B1-11-P3A专项用例实测。
- 无越界:文件无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,仅记录)
- P3-1'|
IsCurrent对session的非阻塞读取依赖prepared发布顺序。Volatile.Write(prepared)与字段写入的顺序经volatile读语义保障正确(acquire),实现无缺陷;仅提示后续若增字段需维持"先写字段后写prepared"约定。位置:PlayerKVLoginContext.cs行54–63/152–159。 - P3-2'|
CompleteFailureAsync在CloseFailed时仍保留原始错误。 已按合同实现(OriginalError);建议4B2接线文档显式引导调用方同时检查CleanupFailed与OriginalError,避免只看Error==CloseFailed丢失根因。文档级建议。 - P3-3'|首次失败run的夹具缺陷记录。 首轮21/1失败系测试夹具token注册错误(自审已记录),非生产缺陷;历史run保留正确。无行动项。
结论
接受4B1实施;无P1/P2;P3三条仅记录。 建议主会话逐条裁决后关闭4B1。4B2(游戏加载与内存隔离)需独立完整方案(业务档校验、空档阻塞策略、初始化副作用、OnInitData时序、401/退出接线)与评审后实施;4B1关闭不放行游戏接线或正式业务KV。