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

59 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 阶段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`确实为"过滤+变换+三索引同步"复合链:
- 未知鱼`GetOrDefault`null→**静默跳过**;`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整体的独立实施复核不在本次范围。