Files
ft/CodexOutput/upserver/阶段4B2检查点2配置隔离与鱼数据派生独立审查-2026-09-23.md

6.5 KiB
Raw Blame History

阶段4B2检查点2配置隔离与鱼数据派生独立审查(D4/D5)

日期:2026-09-23。审查者:ZCode会话。

身份披露:未参与本补审方案编写(由主会话编制)。延续性说明:D4/D5暴露的合同缺口(静态配置隔离、鱼数据派生规则未明确)属修订2附件M03/M18行粒度不足的延续——本审查按源码逐项复核方案断言。

本轮只读+仅新增本文件;未改代码/方案/基线/入口/TODO/日志;无子Agent、无正式服、无PlayMode、无Unity执行(纯静态审查,未运行任何用例)。

基线核对

阶段4B2检查点2配置与派生差异受审基线-2026-09-23.json共23项,逐项重算SHA256——全部一致。

D4(PartnerInfoManager配置隔离):有条件通过

源码事实核实(全部属实):

  • PartnerInfoManager.cs:27:private static Tables _tables { get; set; } = GContext.container.Resolve<Tables>()——静态表缓存自GContext,类型初始化即冻结 ✓
  • :44:public static readonly int BuilderCapacity = _tables.TbEventPartnerConfig.InvitationCount[1]——静态readonly,冷初始化时计算 ✓
  • :46:实例BuildPartners数组长度绑定静态容量 ✓
  • 外部BuilderCapacity数值读取恰为三处(EventPartnerGatherManager.cs:287/1182/1212);RobotInvitationSystem两处(:38/:135);Mining面板仅注释 ✓(308行Init()/309行EventId赋值不受字段→属性影响,盘点口径准确)

评估:冷/热类型初始化问题成立(首次构造临时实例会把临时表图永久留在静态_tables;已初始化则用旧表不服从不epoch快照)。推荐方案(_tables改实例只读+BuilderCapacity改计算属性+实例数组读实例表+AddBuildPartner改读BuildPartners.Length)能封住两种泄露路径;四条否决替代(反射改静态/假模块/只恢复GContext/正式Root预热)理由均成立。批准PartnerInfoManager.cs新增白名单及公开字段→属性变化。

条件(均为实施时强制验收,方案已列,此处重申+补充):

  1. 方案§2.2已列:全量Unity编译、全部引用盘点、预编译程序集依赖核查——不得仅凭编辑器可跑放行。
  2. P3-1(补充):_tables实例化后有随动读取者需在diff中显式列出——:50的ActiveFriendCountThreshold属性与:242–244的实例方法读_tables,实例化后自动转为实例读,行为正确但审查时须可见,避免"只改了两处"的误解。
  3. P3-2(补充):静态属性语义从"类型初始化冻结"变为"每次读当前容器"——固定配置下数值等价(D4-04覆盖),但运行期配置热更场景下两者可返回不同值:作为已知边界写入实施说明,不当作缺陷修复。
  4. D4-02允许的"内存复制配置JSON改InvitationCount"仅限测试epoch内,最终B2-20用未改值表快照——边界正确,实施不得颠倒。

D5(鱼数据派生):通过

源码事实核实(全部属实):DataCenter.PlayerFishData.cs:809–860的SetFishAllData确实为"过滤+变换+三索引同步"复合链:

  • 未知鱼GetOrDefaultnull→静默跳过;AreaID != key→Debug.Log+continue静默跳过(方案改UnsupportedSchema拒绝 ✓)
  • 5字段复制→Quality≤5经FishMaxG修正MaxG(FishMaxG公式与方案§3.2逐项一致:BaseWeight*PointExtraParam*(1+WeightRandom)、Level>0乘WeightMagList[level-1]、Mathf.Round(x*100)/100、min(原MaxG) ✓)
  • Level>0:TbFishCard[FishCardID](缺卡抛异常而非拒绝)、Level=min(Level,MaxLevel)、CardsRequiredList[Level-1]阈值、TotalCard=max ✓
  • mapDataNew/fishData[ID]=同一实例/fishCardData[FishCardID]=TotalCard三索引同步——同卡ID跨鱼/跨地图覆盖使结果依赖字典遍历顺序(方案"同一卡ID不同TotalCard→UnsupportedSchema"正确消除该不确定性)✓

逐项裁决(提示词要求):

  • 旧变换抽到纯Decode:同意——变换是加载语义的一部分,不抽出则Replace后的运行态与PF路径不一致;快照字段清单(鱼ID/AreaID/Quality/BaseWeight/PointExtraParam/WeightRandom/FishCardID+卡MaxLevel/CardsRequiredList/WeightMagList)与变换链读表集合吻合,无遗漏。
  • 原值比较先于变换:同意——"先截断修正后比较"会把真实冲突掩盖为一致,顺序约束正确。
  • 严格拒绝规则(未知鱼/错归属/缺表/下标/同卡冲突→UnsupportedSchema):全部同意——取代旧静默跳过/抛异常,与"KV严于PF"既定政策一致;须计入P2-α迁移盘点(方案§3.1已列)✓。
  • 三套索引一致性+同实例规则:同意——模块内部fishData指向同Map同FishWeightData实例保持旧语义;跨输入/账号不共享。
  • 9槽兼容槽保留不参与派生:同意(与附件§3.2一致)。
  • float舍入:同意——单精度保序+Mathf.Round中点行为对照(0/正负中点/相邻float/大值),不以双精度重排,正确。
  • InitLuck三标量保持0:同意——源码中InitFishLuck/InitBotLuck/BotCount由玩法Init→InitLuck建立而非Load链;4B2只做Load、不运行Init,保持Reset的0是准确边界,不得套用表默认值。

P3-3(补充):D5-02的"最大重量上下界"用例建议明确包含"原MaxG小于计算值时不抬高"(min语义的反向用例),防止实现误写成clamp双向。

遗漏检查

  • M03当前消费键与可变集合:方案覆盖6整数字典、RodGSA六字段(GloveID/RodToSkin/GloveIds/SkinIds/GloveNewIds/SkinNewIds)、Luck三表示、Map动态键——与附件正式版2 M03行一致,未发现遗漏。
  • M18/PartnerInfoManager状态:替换空新实例、GatherData=null、标志清空、不Init——与附件一致。
  • 验证项D4-01~04/D5-01~05/D45-06能证明两项修订且明确"完整B2-20仍需31模块+guard零副作用"——不把局部通过冒充整体 ✓。

结论

  • D4:有条件通过——条件为方案§2.2/§4已列强制项+本报告P3-1/P3-2随动说明;无需方案重报。
  • D5:通过——含上述全部逐项裁决;P3-3为用例补充。
  • 建议:批准PartnerInfoManager白名单扩展;两项均计入下一正式合同版本(沿用D1–D3合并先例);P2-α盘点追加"未知鱼/错归属/同卡冲突的旧静默跳过条目"。本轮仅静态审查;28模块局部实现与4B2整体的独立实施复核不在本次范围。