Files
ft/CodexOutput/upserver/阶段4B1独立实施复核-2026-09-23.md

6.4 KiB
Raw Blame History

阶段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:通过。 逐项:

  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。