docs(upserver): 归档阶段裁决与批量迁移待办

This commit is contained in:
Liubing\LB
2026-09-28 18:17:40 +08:00
parent a6f5e107f8
commit 1c0cdf7a99
376 changed files with 89430 additions and 0 deletions

View File

@@ -0,0 +1,141 @@
# 101两类历史空态最小兼容方案(修订1,待差异补审)
2026-09-28,主会话。前置:[P2-α-A关闭裁决](../P2AlphaAudit/A5-执行复核裁决与关闭.md)。本单元简称P2-α-B,仅代表两类空态兼容,不等于整体迁移政策或阶段4B。
**最新状态:原方案已审并完成B1裁决及B2离线红测,生产代码尚未修改;发现原M15解析顺序不可达,修订1待差异补审后恢复实施。** 已改两个离线测试文件,旧生产代码下77组中3组预期失败,证据见B2-red-result.json。原受审方案副本保存在B2-original-reviewed-plan.md,原审查与清单不重写。保留用户“方案完整评审后编码”的要求。
## 1. 问题、目标与取舍
已有本地PF101共55键,被当前原生产Decode拒绝的两项是:M15 `FishingChallengeData`的JSON null;M22 `GeneralEventPackData.selections`的JSON null。旧业务能产生这两种值,详见[已复核语义盘点](../P2AlphaAudit/A3-空态语义与未消费键裁定.md)。PF102共45键已Valid,且KV102已有后续真实游戏进度。
目标:只修两条加载表示差异,让解码与物化保留旧空态;其余字段、严格格式、集合隔离和事务准入不变。保留所有Raw键和值,不替用户改档。101能否整体Valid,以原样本和冻结配置实测为准;不为追求全绿不断扩大修复。
| 路线 | 取舍 | 决定 |
|---|---|---|
| 精确保留两类null表示 | 改4个已有生产文件;保留存在性、其他字段及列表深复制 | 推荐,作为本方案唯一申请范围 |
| 删除挑战键、礼包null规范成[]或重建礼包 | 表面改动少,丢原表示或覆盖已有礼包字段;存档写入范围更大 | 不采用 |
| 通用可空Schema/迁移框架或补全所有活动入口 | 扩大所有模块接受集合和生命周期改动,无法由两个样本验证 | 不采用 |
不需要新增架构、服务、状态机、配置开关或持久化版本。M15为避免把“键存在但null”冒充“键缺失”,只给现有内部DTO补一个只读标记;不引入通用三态框架。
## 2. 输入与开发边界
- 原样本、cfgData/Tables及关键源码由本目录`design-review-hashes.json`冻结;原A单元证据保持原文。PF101/102不再联网拉取,KV102仅用既有末次备份作历史证据。
- 生产白名单4个已有文件:`Client/Assets/Scripts/Services/PlayerData/PlayerKVSaveSchema.cs`、同目录`PlayerKVDecodedSave.cs`、`Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs`、同目录`BargainPackData.cs`。只改下文指明的Decode/DTO/Replace;不改活动入口、计分、奖励、购买、保存或Reset逻辑。
- 测试白名单3个已有文件:`CodexOutput/upserver/KVGameLoadTests/ActivitySchemaTests.cs`、同目录`D3SchemaTests.cs`、`Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs`(仅UNITY_EDITOR固定验证;包含Bargain专用比较器的null分支)。无新测试工程/新反射入口/新诊断类;不改csproj、asmdef或生成Tables。
- `KVGameLoadValidation.cs`原固定case入口、`KVGameplayLoopSetup.InspectPFBackup`、`unity-source-inspection.cs.txt`及其他测试文件只复用。设计/结果/源码修改前快照放本目录;共享入口/状态/TODO/日志逐检查点更新。
- 任何白名单扩展或第三种拒档原因需要停在具体差异并记录,不得自动放宽。暂不打包,无Play、许可、登录、远端读写或存档导入。地址固定`http://s101.local:18080/`,只用Unity CLI Loop。
## 3. M15:缺键、JSON null、合法对象三者分开
### 3.1 表示合同
| 输入 | IsPresent | 新IsExplicitNull | 受控Reset→Replace后的业务对象 |
|---|---|---|---|
| 缺FishingChallengeData键 | false | false | null;保留原Missing合同 |
| 键值为严格JSON null,允许JSON自身空白 | true | true | null,未激活 |
| 完整合法JSON对象 | true | false | 原全部字段物化和列表深复制 |
| C# null值、空字符串/空白、字符串"null"、数字、布尔、数组、坏JSON | 无成功DTO | 无 | Malformed,不能调用Apply |
**修订1:** 原共用`Json`在PlayerKVSaveSchema:163拒绝根级Null,故原方案“先Json再判断Null”不可达。在现有`errors.Capture(15,0)`内,先仅识别`text != null && text.Trim(' ', '\t', '\r', '\n') == "null"`;命中时调用已有`StrictJsonSyntax.Validate(text)`验证完整原文,再返回ExplicitNull工厂。其余输入沿用原`Object(Json(text))`及全部字段验证。四种trim字符恰为JSON空白,不使用宽松任意Unicode空白,不去掉注释或尾随内容。C# null不命中,仍由Json报Malformed。Raw不使用trim后的字符串。
共用Json/Object/IntegerList/StrictJsonSyntax实现均不修改,没有第二套解析器、包装拼接JSON或其他模块接受范围变化。StrictJsonSyntax本来支持null字面量及全量耗尽检查;局部分支只处理这一终结符,不触碰对象结构。对象内部任一原必填字段缺失/null仍失败。根null与嵌套null有区别:M22根为对象、selections嵌套null原Json可解析,故M22方案无需调整。
PlayerKVChallengeState增加只读`IsExplicitNull`;既有构造末尾增加可选`bool explicitNull=false`以保留旧调用,赋值并禁止`explicitNull && !present`。新增`ExplicitNull()`工厂生成present=true、explicitNull=true、其余默认值及非null空只读列表。Missing保持present=false、explicitNull=false。对象构造仍默认false。此DTO为内部内存表示,不序列化到服务器。
FishingChallengeManager.ReplaceFromKVDecoded仅把原`!state.IsPresent`条件扩为“缺失或显式null”,两者赋业务对象null;合法对象初始化器不改。Reset继续由现有受控加载事务先调用,不在Decode或Replace中启动活动、清理全局状态或写档。仍拒绝state本身为null。
Raw字典由原PlayerKVDecodedSave复制:显式null保留键及原字符串,缺键仍缺键;不能以DTO旗标重建Raw。现有DoSave对业务null序列化为JSON null的行为保持;本轮验证其序列化含义而不调用网络保存。
### 3.2 调用前置、P3-1及限制
[142处逐引用前置锚点](M15-逐引用前置锚点.md)与[机器台账](M15-逐引用前置锚点.json)逐行链接原引用、近邻声明和条件;原始语句/分类仍以[已复核台账](../P2AlphaAudit/A3-M15-read-ledger.json)为准。近邻条件不冒充控制流支配证明;没有可证明门的调用明确保留为“需要有效活动上下文”。不因本次接受null而宣称这些入口都安全。
| 门或前态 | 当前源码锚点(行号以受审源码为准) | 本方案结论 |
|---|---|---|
| fresh/Reset | FishingChallengeManager:63、70~74;Replace:76~98 | 空态不创建默认挑战,不激活配置/异步行为 |
| 初始化 | 同文件CheckInit:237、Init:244、251/256/258、263 | 有活动时原Init可能创建对象;本方案不调用Init,也不承诺将来的活动开启永久保持null |
| 首页显示 | HomeBtnChallenge:67、75;Manager.ShouldOpen:139/143 | 正常OnEnable经过ShouldOpen,后续按钮回调仍依赖活动生命周期 |
| 状态观察 | Manager.ChallengeState:1028/1034;IsSettleState:1062/1066 | null返回state0;短路不进入时间判断 |
| 局部空判断 | Manager:281、572、605、844、950、963、1074、1089 | 仅对应函数/分支的保护,不覆盖所有异步续体 |
| 奖励准入 | UserService.KVFishingReward:127 | 非null才检查挑战状态,本方案不改奖励代码 |
| 直接/别名读取 | Manager.IsFirstOpen:110、IsTimeAllow:169等;ChallengeMatchSceneRoad:81~82 | 依赖已初始化活动及合法场景进入;未证明任意场景调用都安全 |
| 资源异步入口 | Manager.InChallenge:510、514及后续资源加载 | 状态0仍可能触发加载,限制保持;不批准101真实挑战验证 |
已有业务对象→Reset→显式null必须验证;fresh→显式null与fresh→缺键都为空但DTO/Raw存在性不同。禁止把此结论用于未Reset旧对象、热换号或任意跨账号异步回调。
## 4. M22:保留列表null而保留整个礼包
只在DecodeBargain局部读取`selections` token:属性存在且token.Type为Null才允许null;属性不存在(C# null)仍让原IntegerList拒绝。数组仍原IntegerList:字符串/对象/浮点/越界/元素null不接受。GeneralEventPackData根null、缺其余字段、未知或非PackType=3的redirectID仍走现有错误分类。
PlayerKVBargainState构造仅改变selections赋值:输入null保存null;否则仍复制为只读列表,包括空数组。Missing()仍传空数组,不改变“整个礼包缺键”的IsPresent=false和默认字段。
BargainPackData.ReplaceFromKVDecoded仅改变selections字段:state.selections为null则null,否则new List深复制。其余11整数、2布尔、1浮点及_kvLoadPresent保持原赋值。SnapshotForKVValidation现有构造调用自然保留null,不需要新增数据结构或setter。Reset保持新空List和_kvLoadPresent=false。不改旧LoadData/UpdateData/SaveData;序列化应保留selections:null,不能自动转[]。
| 场景 | DTO/实际对象/Snapshot | 其他合同 |
|---|---|---|
| 根key缺失 | IsPresent=false,列表空 | Reset与缺礼包既有合同 |
| 完整对象,selections=null | IsPresent=true,列表null | 所有其他字段保持原值 |
| 完整对象,selections=[] | IsPresent=true,独立空集合 | 不与null混同 |
| 完整对象,selections=[7,9] | 只读DTO、独立业务List、独立Snapshot | 任一可变列表改变不能影响另两者 |
| selections属性缺失/类型错或其余字段坏 | 原Malformed;错误redirect原UnsupportedSchema | 不输出可Apply状态 |
旧101 redirect=4056001,冻结配置PackType=3/MaxCount=1;只作现有输入证据,不硬编码账号ID、礼包ID或字符串样本特判。
## 5. 验收矩阵与复用测试
先更新获审测试并记录旧生产代码下红测,再实施上述4文件最小差异并绿测。旧测试中M15“根null拒绝”及M22“selections属性null拒绝”两条期望需要精确改写;其他对象字段null/缺字段及M26/M27必须仍拒绝,不能整体删除严格循环。
| 编号 | 测试与证据 | 通过条件 |
|---|---|---|
| T1 | ActivitySchemaTests原RunActivitySchemaTests增加M15根空态边界 | 缺键/JSON null/带四种JSON空白null/对象四组DTO值正确;C# null、空、坏JSON、"null"字符串、数值、布尔、数组拒绝;修订1补NBSP/U+FEFF等非JSON空白、注释、null null、null尾逗号/对象注入拒绝,以及原共用Json("null")仍拒绝的隔离断言。对象每字段缺失/null仍拒绝。复用同一partial Tests已有Aggregate/ModernRaw,断言完整Decode的Data.Raw逐UTF8字节保留带空白null、模块外探针键原值及缺键差异,不修改AggregateTests文件 |
| T2 | D3SchemaTests原M22段与DTO集合测试 | null、[]、非空区分;缺属性/非法数组元素及其余字段错误继续拒绝;DTO非空深复制;null不抛异常;错误PackType不绕过检查 |
| T3 | 原Editor固定case M15-M26-M27,在RunActivities追加 | fresh/null、合法对象→Reset→null、再次对象→Reset→缺键均正确;IsInactiveForKVValidation为true;ChallengeState=0/ShouldOpen=false;JsonConvert序列化业务对象为null;M26/M27原断言不变 |
| T4 | 原Editor固定case M21-M22-M24,在RunD3追加 | 对象→null列表→[]→非空→Reset/缺键;每次Snapshot逐字段比对所有礼包字段与IsPresent;验证双向修改隔离,序列化保留null;原其他模块回归不删 |
| T5 | KVGameLoadTests全量,已有完整Decode/事务回归 | 运行精确DLL;旧严格语法/AD/鱼配置/失败不发布及其他模块不回归。T1/T2所在既有方法自动被现有Program执行,无新测试注册 |
| T6 | 原Editor固定B2-20完整事务case;原固定InspectPFBackup读两个未改样本 | B2-20回归通过;101两条目标诊断消失,102仍Valid;若出现第三问题记录并停止扩大。保留原样本值hash与Decode Raw复制的现有合同,附T1/T2 Raw输入前后相等断言;没有移除模块外键 |
| T7 | Unity CLI Loop重编译+现有ServiceHost编译命令 | Editor无新增编译错误,SERVICE_HOST成功;不更改非Editor历史问题或工程配置。失败保留首败输出,不用重复运行掩盖 |
| T8 | diff/hash/运行边界 | 生产4文件仅约定成员、测试3文件仅新用例/精确期望及Bargain专用比较分支;配置和备份不变;无Play/permit/正式存档接口请求;结束环境恢复 |
测试无效保护:红测必须是预期兼容断言失败,编译失败或错误apphost启动不能充当红证据。已有共享apphost曾指向其他程序集;本轮使用`dotnet <本次明确构建的KVGameLoadTests.dll>`并记录DLL hash/时间/测试组名。没有涉及的奖励测试不为凑数重跑;只有新失败或发现依赖时再扩大回归并说明理由。
测试比较器已发现必要适配:PlayerKVEditorValidationHost:107的Bargain专用Same使用SameList,而:117通用SameList直接取Count,不能用它比较两个null列表。只在Bargain专用Same的selections子表达式中区分两者都null、仅一方null、双方非null后调用原SameList;其余14字段比较仍执行。通用SameList与其他模块不改。新增null/null相等、null/[]不等、其他字段不同不等的断言,避免比较器抛错或漏检冒充业务兼容通过。
离线构建沿用`dotnet build CodexOutput/upserver/KVGameLoadTests/KVGameLoadTests.csproj --no-restore`,运行该工程实际输出DLL;SERVICE_HOST沿用`dotnet build CodexOutput/upserver/KVGameLoadTests/ServiceHost/ServiceHost.csproj --no-restore`。不依赖dotnet run选择共享apphost,不新增测试工程。原Editor入口接受的case名称不变。
T3/T4仅对隔离真实对象调用受控Reset/Replace/Snapshot,不调用Init/CheckInit、SaveData/DoSave、购买或资源场景。沿用原测试容器与finally恢复,不发布游戏句柄。M15直接IsInactive观察及不会联网的ShouldOpen/ChallengeState在空态验证;不造真实账户身份。若原case共享容器恢复失败,停止并记录,不能继续Inspect。
## 6. Editor与数据权限
执行前通过CLI读取版本、进程和playing/enabled/ready/run/permit;90064只是上轮已测进程,不假设审批后仍相同。必须空闲、无运行许可、无活动validation/GContext,保留11个历史claimed,不改启用静态字段绕门。
需要重编译使用现有Unity CLI Loop;编译完成确认空闲后跑固定用例与Inspect。若状态不满足或需要新进程,先确认已退出Play/排水/无许可,再正常关闭重启;不用强杀或launch --restart。不得在方案未审时预写测试或更改源码触发编译。
本方案只申请本地修订和离线/Editor隔离测试,不申请101真实登录、PF/KV导入、远端Get/Set/Delete、收费或补偿。101/102旧授权不替代本轮限定范围。正式迁移前身份/权威源/独占窗口/CAS缺口及产品处理政策仍未解决。
## 7. 三条P3、未知键和102分叉
P3-1见§3及142行附件。P3-2/P3-3见[定位尝试与102分叉时间线](定位尝试与102分叉时间线.md)。引用原盘点已封存证据,不重新搜索后把历史结果写成当前新证据。
五种未定位键仍保留,不在本轮Schema新增模块或删键。其他26种模块外键与Present覆盖不足保持专项未决。102从旧PF导入后经历装备、消费及收鱼,末次KV备份已分叉;本次本地PF102复测只是兼容回归,不是重新认定PF为当前权威,更不能把旧档写回。
## 8. 检查点及关闭条件
| 检查点 | 责任与交付 |
|---|---|
| B0 当前方案准备 | 完整设计、142引用锚点、定位/时间线附件、精确白名单、作者自审、提示词及受审hash;尚未编码 |
| B1 独立方案复核返回后 | 主会话核hash逐条裁决、明确批准版本;保存7个待改文件前态,记录Editor准入与配置/样本基线 |
| B2 红测与最小编码 | 仅7文件白名单;先记录预期红测,再修改4个生产文件;T1/T2及diff检查 |
| B3 验证 | T3~T8,记录当前UTC/版本/cfg指纹/原始输出,真实样本只读;发现第三问题登记,不自动扩大 |
| B4 作者交付 | 结果、首败、限制、最终diff与hash、独立实施复核提示词;更新共享文档 |
| B5 独立实施复核 | 主会话逐条裁决后关闭两类兼容;不关闭整体P2-α/G4,不推为101可正式启用 |
每个检查点更新开发日志和入口/TODO。若无阻塞可继续本已批准检查点,不反复征求同一授权。需要新增生产改动时交精确补充方案,不以“让101跑通”为理由顺手重构。
## 9. 作者自审
- 已完成上下文、三路线取舍、完整合同和测试设计;无视觉设计问题,无需视觉伴侣。既有用户约定确定目标,无需重复问选择;采用brainstorming内容方法,文档放既有upserver目录,不自动提交共享Git工作区。
- 接受根null与接受对象字段null严格分开;C# null/缺属性不是JSON null。M15新增旗标保持Raw存在性,M22所有其他值及深复制保留。
- 生产改动4个已有文件;测试复用3个已有文件和固定入口;不增加Schema框架、运行许可机制或迁移工具。
- 方案没有声称null会让所有玩法入口安全,没有批准101导入或修改当前102;报告附录中的未证明项仍是未证明。
- 原B1已执行,B2仅完成旧生产代码的离线红测;修订1恢复实施前须差异补审裁决。B3~B5尚未执行。当前没有任何生产源码修改,两个离线测试文件处于有意保留的红测状态。

View File

@@ -0,0 +1,31 @@
# P2-α-B方案审查裁决与实施批准
2026-09-28,主会话。独立报告通过、无P1/P2,用户要求继续;批准已审方案B1~B4实施,B4之后交独立实施复核。
## 基线
修改任何受审文件前独立重算1277/1277项SHA256及长度,全部一致,见B1-review-verification.json。方案清单SHA256:F9DB3DE824F24CA53467A0DB62FE07B6455C4655890C903699CD2A13D51D39EF;报告SHA256:413DCADED1A71325F117CDF3883E5BDD45D22C7A3ABA292F2E1EB21E764EBDCC。
## 逐条裁决
| 项 | 主会话裁决 |
|---|---|
| Q1 最小白名单 | 接受;4生产+3测试已有文件,仅约定成员,Raw复制/保存/活动入口不改 |
| Q2 M15三态 | 接受;严格Json后仅Null分支,保留IsPresent/IsExplicitNull区分;显式null且缺失组合拒绝 |
| Q3 Replace前置 | 接受;原Reset先行,null不激活活动;142锚点不是全调用安全证明,不运行挑战场景 |
| Q4 M22 | 接受;只接受存在属性的JSON null,缺属性/其他字段/PackType严格不变,null与[]分开且集合继续深复制 |
| Q5 测试 | 接受;T1~T8、预期断言红测、精确DLL、Bargain专用比较器适配;其余严格负例不能删除 |
| Q6 运行边界 | 接受;实际读Editor准入,只用CLI Loop和固定入口;无Play/许可/正式存档接口/导入/打包 |
| Q7 附件 | 接受;5键、102分叉及产品政策继续保留,既有授权不扩大为当前101正式启用 |
| Q8 检查点 | 接受;B1批准→B2红绿最小修改→B3验证→B4交付,B5后由主会话裁决关闭 |
| P3-1 Raw逐字节 | 采纳;T1显式比较UTF8字节,包含空白null变体及模块外探针键,不从DTO反推Raw |
| P3-2 XML注释 | 采纳;新增标记及工厂注明内部内存表示、不参与服务器序列化 |
| P3-3 第三诊断停点 | 采纳;若出现新诊断,记录JSON路径及方案§2“任何白名单扩展或第三种拒档原因需要停在具体差异并记录,不得自动放宽”,不能扩大修复 |
## 实施范围
拟实施白名单JSON为原审查输入,保持原字节;本裁决将其中7文件限定成员批准实施,不重写历史implementationApproved=false。没有新增框架、项目、迁移工具或编辑器反射入口。修改前7文件完整快照保存在before目录;源码快照供差异复核,不是追加生产文件。
红测先用旧API断言两种应接受空态,避免引用尚未存在属性造成编译失败冒充红测;生产实现后再加新标记/非法组合断言。此为同一已审测试合同的分步落实,不改变接受集合。
整体P2-α/G4仍未关闭。本轮不请求新真实账号数据,不覆盖102进度,不创建运行许可。

View File

@@ -0,0 +1,139 @@
# 101两类历史空态最小兼容方案(待完整独立评审)
2026-09-28,主会话。前置:[P2-α-A关闭裁决](../P2AlphaAudit/A5-执行复核裁决与关闭.md)。本单元简称P2-α-B,仅代表两类空态兼容,不等于整体迁移政策或阶段4B。
**当前只完成设计;尚未批准或修改生产/测试源码。** 用户已要求完成后自动进入下一步,本次据此推进完整方案准备;保留用户“方案完整评审后编码”的要求。
## 1. 问题、目标与取舍
已有本地PF101共55键,被当前原生产Decode拒绝的两项是:M15 `FishingChallengeData`的JSON null;M22 `GeneralEventPackData.selections`的JSON null。旧业务能产生这两种值,详见[已复核语义盘点](../P2AlphaAudit/A3-空态语义与未消费键裁定.md)。PF102共45键已Valid,且KV102已有后续真实游戏进度。
目标:只修两条加载表示差异,让解码与物化保留旧空态;其余字段、严格格式、集合隔离和事务准入不变。保留所有Raw键和值,不替用户改档。101能否整体Valid,以原样本和冻结配置实测为准;不为追求全绿不断扩大修复。
| 路线 | 取舍 | 决定 |
|---|---|---|
| 精确保留两类null表示 | 改4个已有生产文件;保留存在性、其他字段及列表深复制 | 推荐,作为本方案唯一申请范围 |
| 删除挑战键、礼包null规范成[]或重建礼包 | 表面改动少,丢原表示或覆盖已有礼包字段;存档写入范围更大 | 不采用 |
| 通用可空Schema/迁移框架或补全所有活动入口 | 扩大所有模块接受集合和生命周期改动,无法由两个样本验证 | 不采用 |
不需要新增架构、服务、状态机、配置开关或持久化版本。M15为避免把“键存在但null”冒充“键缺失”,只给现有内部DTO补一个只读标记;不引入通用三态框架。
## 2. 输入与开发边界
- 原样本、cfgData/Tables及关键源码由本目录`design-review-hashes.json`冻结;原A单元证据保持原文。PF101/102不再联网拉取,KV102仅用既有末次备份作历史证据。
- 生产白名单4个已有文件:`Client/Assets/Scripts/Services/PlayerData/PlayerKVSaveSchema.cs`、同目录`PlayerKVDecodedSave.cs`、`Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs`、同目录`BargainPackData.cs`。只改下文指明的Decode/DTO/Replace;不改活动入口、计分、奖励、购买、保存或Reset逻辑。
- 测试白名单3个已有文件:`CodexOutput/upserver/KVGameLoadTests/ActivitySchemaTests.cs`、同目录`D3SchemaTests.cs`、`Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs`(仅UNITY_EDITOR固定验证;包含Bargain专用比较器的null分支)。无新测试工程/新反射入口/新诊断类;不改csproj、asmdef或生成Tables。
- `KVGameLoadValidation.cs`原固定case入口、`KVGameplayLoopSetup.InspectPFBackup`、`unity-source-inspection.cs.txt`及其他测试文件只复用。设计/结果/源码修改前快照放本目录;共享入口/状态/TODO/日志逐检查点更新。
- 任何白名单扩展或第三种拒档原因需要停在具体差异并记录,不得自动放宽。暂不打包,无Play、许可、登录、远端读写或存档导入。地址固定`http://s101.local:18080/`,只用Unity CLI Loop。
## 3. M15:缺键、JSON null、合法对象三者分开
### 3.1 表示合同
| 输入 | IsPresent | 新IsExplicitNull | 受控Reset→Replace后的业务对象 |
|---|---|---|---|
| 缺FishingChallengeData键 | false | false | null;保留原Missing合同 |
| 键值为严格JSON null,允许JSON自身空白 | true | true | null,未激活 |
| 完整合法JSON对象 | true | false | 原全部字段物化和列表深复制 |
| C# null值、空字符串/空白、字符串"null"、数字、布尔、数组、坏JSON | 无成功DTO | 无 | Malformed,不能调用Apply |
DecodeChallenge在现有`errors.Capture(15,0)`内先调用原严格`Json(text)`一次,仅token.Type为Null走新工厂;其余仍交原Object及全部字段验证。不修改共用Json/Object/IntegerList,不能用宽松反序列化或`string.IsNullOrEmpty`兜底。对象内部任一原必填字段缺失/null仍失败。
PlayerKVChallengeState增加只读`IsExplicitNull`;既有构造末尾增加可选`bool explicitNull=false`以保留旧调用,赋值并禁止`explicitNull && !present`。新增`ExplicitNull()`工厂生成present=true、explicitNull=true、其余默认值及非null空只读列表。Missing保持present=false、explicitNull=false。对象构造仍默认false。此DTO为内部内存表示,不序列化到服务器。
FishingChallengeManager.ReplaceFromKVDecoded仅把原`!state.IsPresent`条件扩为“缺失或显式null”,两者赋业务对象null;合法对象初始化器不改。Reset继续由现有受控加载事务先调用,不在Decode或Replace中启动活动、清理全局状态或写档。仍拒绝state本身为null。
Raw字典由原PlayerKVDecodedSave复制:显式null保留键及原字符串,缺键仍缺键;不能以DTO旗标重建Raw。现有DoSave对业务null序列化为JSON null的行为保持;本轮验证其序列化含义而不调用网络保存。
### 3.2 调用前置、P3-1及限制
[142处逐引用前置锚点](M15-逐引用前置锚点.md)与[机器台账](M15-逐引用前置锚点.json)逐行链接原引用、近邻声明和条件;原始语句/分类仍以[已复核台账](../P2AlphaAudit/A3-M15-read-ledger.json)为准。近邻条件不冒充控制流支配证明;没有可证明门的调用明确保留为“需要有效活动上下文”。不因本次接受null而宣称这些入口都安全。
| 门或前态 | 当前源码锚点(行号以受审源码为准) | 本方案结论 |
|---|---|---|
| fresh/Reset | FishingChallengeManager:63、70~74;Replace:76~98 | 空态不创建默认挑战,不激活配置/异步行为 |
| 初始化 | 同文件CheckInit:237、Init:244、251/256/258、263 | 有活动时原Init可能创建对象;本方案不调用Init,也不承诺将来的活动开启永久保持null |
| 首页显示 | HomeBtnChallenge:67、75;Manager.ShouldOpen:139/143 | 正常OnEnable经过ShouldOpen,后续按钮回调仍依赖活动生命周期 |
| 状态观察 | Manager.ChallengeState:1028/1034;IsSettleState:1062/1066 | null返回state0;短路不进入时间判断 |
| 局部空判断 | Manager:281、572、605、844、950、963、1074、1089 | 仅对应函数/分支的保护,不覆盖所有异步续体 |
| 奖励准入 | UserService.KVFishingReward:127 | 非null才检查挑战状态,本方案不改奖励代码 |
| 直接/别名读取 | Manager.IsFirstOpen:110、IsTimeAllow:169等;ChallengeMatchSceneRoad:81~82 | 依赖已初始化活动及合法场景进入;未证明任意场景调用都安全 |
| 资源异步入口 | Manager.InChallenge:510、514及后续资源加载 | 状态0仍可能触发加载,限制保持;不批准101真实挑战验证 |
已有业务对象→Reset→显式null必须验证;fresh→显式null与fresh→缺键都为空但DTO/Raw存在性不同。禁止把此结论用于未Reset旧对象、热换号或任意跨账号异步回调。
## 4. M22:保留列表null而保留整个礼包
只在DecodeBargain局部读取`selections` token:属性存在且token.Type为Null才允许null;属性不存在(C# null)仍让原IntegerList拒绝。数组仍原IntegerList:字符串/对象/浮点/越界/元素null不接受。GeneralEventPackData根null、缺其余字段、未知或非PackType=3的redirectID仍走现有错误分类。
PlayerKVBargainState构造仅改变selections赋值:输入null保存null;否则仍复制为只读列表,包括空数组。Missing()仍传空数组,不改变“整个礼包缺键”的IsPresent=false和默认字段。
BargainPackData.ReplaceFromKVDecoded仅改变selections字段:state.selections为null则null,否则new List深复制。其余11整数、2布尔、1浮点及_kvLoadPresent保持原赋值。SnapshotForKVValidation现有构造调用自然保留null,不需要新增数据结构或setter。Reset保持新空List和_kvLoadPresent=false。不改旧LoadData/UpdateData/SaveData;序列化应保留selections:null,不能自动转[]。
| 场景 | DTO/实际对象/Snapshot | 其他合同 |
|---|---|---|
| 根key缺失 | IsPresent=false,列表空 | Reset与缺礼包既有合同 |
| 完整对象,selections=null | IsPresent=true,列表null | 所有其他字段保持原值 |
| 完整对象,selections=[] | IsPresent=true,独立空集合 | 不与null混同 |
| 完整对象,selections=[7,9] | 只读DTO、独立业务List、独立Snapshot | 任一可变列表改变不能影响另两者 |
| selections属性缺失/类型错或其余字段坏 | 原Malformed;错误redirect原UnsupportedSchema | 不输出可Apply状态 |
旧101 redirect=4056001,冻结配置PackType=3/MaxCount=1;只作现有输入证据,不硬编码账号ID、礼包ID或字符串样本特判。
## 5. 验收矩阵与复用测试
先更新获审测试并记录旧生产代码下红测,再实施上述4文件最小差异并绿测。旧测试中M15“根null拒绝”及M22“selections属性null拒绝”两条期望需要精确改写;其他对象字段null/缺字段及M26/M27必须仍拒绝,不能整体删除严格循环。
| 编号 | 测试与证据 | 通过条件 |
|---|---|---|
| T1 | ActivitySchemaTests原RunActivitySchemaTests增加M15根空态边界 | 缺键/JSON null/带空白null/对象四组DTO值正确;C# null、空、坏JSON、"null"字符串、数值、布尔、数组拒绝;对象每字段缺失/null仍拒绝。复用同一partial Tests已有Aggregate/ModernRaw,断言完整Decode的Data.Raw保留带空白null、模块外探针键原值及缺键差异,不修改AggregateTests文件 |
| T2 | D3SchemaTests原M22段与DTO集合测试 | null、[]、非空区分;缺属性/非法数组元素及其余字段错误继续拒绝;DTO非空深复制;null不抛异常;错误PackType不绕过检查 |
| T3 | 原Editor固定case M15-M26-M27,在RunActivities追加 | fresh/null、合法对象→Reset→null、再次对象→Reset→缺键均正确;IsInactiveForKVValidation为true;ChallengeState=0/ShouldOpen=false;JsonConvert序列化业务对象为null;M26/M27原断言不变 |
| T4 | 原Editor固定case M21-M22-M24,在RunD3追加 | 对象→null列表→[]→非空→Reset/缺键;每次Snapshot逐字段比对所有礼包字段与IsPresent;验证双向修改隔离,序列化保留null;原其他模块回归不删 |
| T5 | KVGameLoadTests全量,已有完整Decode/事务回归 | 运行精确DLL;旧严格语法/AD/鱼配置/失败不发布及其他模块不回归。T1/T2所在既有方法自动被现有Program执行,无新测试注册 |
| T6 | 原Editor固定B2-20完整事务case;原固定InspectPFBackup读两个未改样本 | B2-20回归通过;101两条目标诊断消失,102仍Valid;若出现第三问题记录并停止扩大。保留原样本值hash与Decode Raw复制的现有合同,附T1/T2 Raw输入前后相等断言;没有移除模块外键 |
| T7 | Unity CLI Loop重编译+现有ServiceHost编译命令 | Editor无新增编译错误,SERVICE_HOST成功;不更改非Editor历史问题或工程配置。失败保留首败输出,不用重复运行掩盖 |
| T8 | diff/hash/运行边界 | 生产4文件仅约定成员、测试3文件仅新用例/精确期望及Bargain专用比较分支;配置和备份不变;无Play/permit/正式存档接口请求;结束环境恢复 |
测试无效保护:红测必须是预期兼容断言失败,编译失败或错误apphost启动不能充当红证据。已有共享apphost曾指向其他程序集;本轮使用`dotnet <本次明确构建的KVGameLoadTests.dll>`并记录DLL hash/时间/测试组名。没有涉及的奖励测试不为凑数重跑;只有新失败或发现依赖时再扩大回归并说明理由。
测试比较器已发现必要适配:PlayerKVEditorValidationHost:107的Bargain专用Same使用SameList,而:117通用SameList直接取Count,不能用它比较两个null列表。只在Bargain专用Same的selections子表达式中区分两者都null、仅一方null、双方非null后调用原SameList;其余14字段比较仍执行。通用SameList与其他模块不改。新增null/null相等、null/[]不等、其他字段不同不等的断言,避免比较器抛错或漏检冒充业务兼容通过。
离线构建沿用`dotnet build CodexOutput/upserver/KVGameLoadTests/KVGameLoadTests.csproj --no-restore`,运行该工程实际输出DLL;SERVICE_HOST沿用`dotnet build CodexOutput/upserver/KVGameLoadTests/ServiceHost/ServiceHost.csproj --no-restore`。不依赖dotnet run选择共享apphost,不新增测试工程。原Editor入口接受的case名称不变。
T3/T4仅对隔离真实对象调用受控Reset/Replace/Snapshot,不调用Init/CheckInit、SaveData/DoSave、购买或资源场景。沿用原测试容器与finally恢复,不发布游戏句柄。M15直接IsInactive观察及不会联网的ShouldOpen/ChallengeState在空态验证;不造真实账户身份。若原case共享容器恢复失败,停止并记录,不能继续Inspect。
## 6. Editor与数据权限
执行前通过CLI读取版本、进程和playing/enabled/ready/run/permit;90064只是上轮已测进程,不假设审批后仍相同。必须空闲、无运行许可、无活动validation/GContext,保留11个历史claimed,不改启用静态字段绕门。
需要重编译使用现有Unity CLI Loop;编译完成确认空闲后跑固定用例与Inspect。若状态不满足或需要新进程,先确认已退出Play/排水/无许可,再正常关闭重启;不用强杀或launch --restart。不得在方案未审时预写测试或更改源码触发编译。
本方案只申请本地修订和离线/Editor隔离测试,不申请101真实登录、PF/KV导入、远端Get/Set/Delete、收费或补偿。101/102旧授权不替代本轮限定范围。正式迁移前身份/权威源/独占窗口/CAS缺口及产品处理政策仍未解决。
## 7. 三条P3、未知键和102分叉
P3-1见§3及142行附件。P3-2/P3-3见[定位尝试与102分叉时间线](定位尝试与102分叉时间线.md)。引用原盘点已封存证据,不重新搜索后把历史结果写成当前新证据。
五种未定位键仍保留,不在本轮Schema新增模块或删键。其他26种模块外键与Present覆盖不足保持专项未决。102从旧PF导入后经历装备、消费及收鱼,末次KV备份已分叉;本次本地PF102复测只是兼容回归,不是重新认定PF为当前权威,更不能把旧档写回。
## 8. 检查点及关闭条件
| 检查点 | 责任与交付 |
|---|---|
| B0 当前方案准备 | 完整设计、142引用锚点、定位/时间线附件、精确白名单、作者自审、提示词及受审hash;尚未编码 |
| B1 独立方案复核返回后 | 主会话核hash逐条裁决、明确批准版本;保存7个待改文件前态,记录Editor准入与配置/样本基线 |
| B2 红测与最小编码 | 仅7文件白名单;先记录预期红测,再修改4个生产文件;T1/T2及diff检查 |
| B3 验证 | T3~T8,记录当前UTC/版本/cfg指纹/原始输出,真实样本只读;发现第三问题登记,不自动扩大 |
| B4 作者交付 | 结果、首败、限制、最终diff与hash、独立实施复核提示词;更新共享文档 |
| B5 独立实施复核 | 主会话逐条裁决后关闭两类兼容;不关闭整体P2-α/G4,不推为101可正式启用 |
每个检查点更新开发日志和入口/TODO。若无阻塞可继续本已批准检查点,不反复征求同一授权。需要新增生产改动时交精确补充方案,不以“让101跑通”为理由顺手重构。
## 9. 作者自审
- 已完成上下文、三路线取舍、完整合同和测试设计;无视觉设计问题,无需视觉伴侣。既有用户约定确定目标,无需重复问选择;采用brainstorming内容方法,文档放既有upserver目录,不自动提交共享Git工作区。
- 接受根null与接受对象字段null严格分开;C# null/缺属性不是JSON null。M15新增旗标保持Raw存在性,M22所有其他值及深复制保留。
- 生产改动4个已有文件;测试复用3个已有文件和固定入口;不增加Schema框架、运行许可机制或迁移工具。
- 方案没有声称null会让所有玩法入口安全,没有批准101导入或修改当前102;报告附录中的未证明项仍是未证明。
- 审批对象为本方案及附件/白名单/基线;目前未执行B1~B5,待下一份独立方案报告。

View File

@@ -0,0 +1,14 @@
# B2修订1裁决与恢复实施
2026-09-28,主会话。补审接受,无P1/P2;修改受审文件前重算1301/1301项hash/字节一致,见B2-revision1-review-verification.json。清单SHA256:30EDAE97606F62148D6A97C482C30FFB360E29B604A0BD521921CFDF13EF23E7;报告SHA256:2DE446158C4C56A280D82E5E8091BF7A31136D6A524CB448E442AA780A6ED076。
| 补审项 | 裁决 |
|---|---|
| Q1 原阻塞/严格语法 | 接受;Json返回前拒根null确实存在,复用现有StrictJsonSyntax的Literal和全量耗尽检查 |
| Q2 局部修订 | 接受;仅DecodeChallenge候选分支,验证原文→ExplicitNull;其他输入原Object(Json),共用helper/Raw复制不动 |
| Q3 负例与M22 | 接受;增加非JSON空白/注释/额外token/注入及共用Json根null仍拒断言;M22仅嵌套null、原方案不变 |
| Q4 白名单/红测 | 接受;4生产+3测试,旧生产77组3预期失败证据沿用,不重跑同一红测或抹除首败 |
| P3-1 四空白 | 采纳;Trim只传空格、tab、CR、LF;新增VT/FF/NBSP等拒绝断言 |
| P3-2 解释注释 | 采纳;局部分支说明共用Json拒绝根null,所以识别前置;不写易过期硬编码源码行号 |
恢复原B2~B4,批准按主方案修订1落码。原八项三P3继续有效,原方案副本和两轮清单保持。仍无Play、许可、正式接口、存档导入或打包权限扩展。每检查点更新日志/入口/TODO;B4后独立实施复核再由主会话关闭本单元。

View File

@@ -0,0 +1,29 @@
# B2解析前置遗漏与修订1补审
2026-09-28,主会话发现并记录。本次是原方案技术遗漏,不是第三类真实存档问题;不把既有方案“通过”当作可以越过矛盾编码的依据。
## 已执行与证据
- B1:1277/1277基线一致,八项及三P3裁决,7文件修改前快照保存。
- CLI确认Unity 2022.3.62f2c1、当前项目、playing/enabled/ready=false、run=null、permit=false,11 claimed;未进入Play。
- B2:仅修改两个已批准的离线测试文件,保留全部其他模块严格断言。构建0错误0警告,精确执行KVGameLoadTests.dll,77组/1717次断言,74组通过、3组预期失败。
- 三组失败:M22-null-empty-list-preserves-fields-and-strictness(null列表应可接受)、M15-activity-all-saved-fields-strict(根null应为存在空态)、M15-explicit-null-raw-and-strict-boundaries(聚合应Valid)。不是编译失败,不是apphost错用。
- 原始结果:B2-red-build.log、B2-red-tests.log、B2-red-result.json、B2-red-artifact.json。B2-red-code-scope.json确认4生产文件和Editor验证host未变;只有2离线测试文件改变。
## 精确遗漏
PlayerKVSaveSchema.cs:145~168的共用Json先验证语法,再在:163执行`if (reader.Read() || result.Type == JTokenType.Null) throw ...`。原方案§3.1及原独立审查Q2假定Json能返回Null token,该假定错误。直接照旧方案编码,101的M15仍Malformed。
我编写原方案时漏核这一返回前检查,本轮在修改生产代码前发现。不能修改共用Json来消除错误,因为那会改变其他模块和原“共用helper不变”的边界。
## 唯一修订与理由
原4生产+3测试白名单不变,仅修订DecodeChallenge内部的识别顺序:在Capture内对非C# null的文本,仅以四种JSON空白修剪后等于字面量null作为候选;对原字符串调用既有StrictJsonSyntax.Validate后返回ExplicitNull。其他输入保持原Object(Json(text))调用。共用Json、StrictJsonSyntax、Raw复制不修改;M22不受此问题影响。
`StrictJsonSyntax.Validate`现有全量耗尽检查以及`Value`的Literal("null")可验证这个唯一标量;这不是复制JSON解析器。非JSON空白、注释、额外token、尾逗号和注入字符串仍走原严格拒绝;原Raw空白字节仍保留。修订1新增这些边界断言以及原Json("null")仍拒绝的隔离断言,待补审批准后加入。
## 当前停点
暂停生产编码及后续B3。已经获审的红测代码和失败证据保留,不回退掩盖,也不称测试全绿。未请求服务器、未写存档、未运行真实101/102、未触发Play或打包。下一责任侧为独立方案差异补审;补审通过后主会话裁决即可继续原B2~B4,无需重审已关闭A。
补审对象为修订1§3.1、T1增量、本文及当前红测改动;其他八项/三P3已裁决的合同继续有效。原方案副本及原受审清单保持,可区分这次主动修正而非暗改受审版本。

View File

@@ -0,0 +1,51 @@
# P2-α-B实施交付与验收
2026-09-28,主会话。**B1~B4完成,待B5独立实施复核,尚未关闭本兼容单元。** 原101/102本地样本均Valid,未导入或真实启用101。
## 实际改动与原因
仅原白名单4个生产文件,文本diff合计新增18行、删除5行(含替换与注释),没有新生产文件或通用框架:
| 文件 | 改动 |
|---|---|
| PlayerKVSaveSchema.cs | M15仅四种JSON空白包裹null→既有StrictJsonSyntax验证原文→ExplicitNull;其余原Object(Json)。M22仅允许存在selections属性的JSON null |
| PlayerKVDecodedSave.cs | M15内部IsExplicitNull/工厂及非法组合检查、说明不参与服务器序列化;M22 null保留、非null仍复制为只读列表 |
| FishingChallengeManager.cs | Replace的一个条件扩展,显式null物化为业务null;活动/场景/计分/保存/Reset逻辑不改 |
| BargainPackData.cs | Replace的一个列表赋值兼容null;其他14字段、存在标记、Snapshot调用与Reset不改 |
共用Json与StrictJsonSyntax实现、DecodedSave的Raw复制保持原文。7文件前态在before;每文件diff及B2-source-diff-summary.json、B4-scope-verification.json可复核。其他待解决模块/生命周期问题没有顺带改动。
测试只改3个已批准文件:ActivitySchemaTests、D3SchemaTests、原EditorValidationHost。Bargain专用Same新增列表null比较分支,通用SameList不动;固定case内读取实际私有_data值验证原SaveData序列化形状及可变列表隔离,未调用SaveData、没有新增可传入任意目标的反射/CLI入口。
## 评审与首败保留
- 原方案1277/1277基线一致,八项与三P3裁决批准。
- 原方案遗漏Json拒根null的前置条件:B2先发现并停止生产编码,修订1补审接受,1301/1301基线一致,四项与两P3裁决后恢复;原文与两轮清单/报告不覆盖。
- 红测:构建成功,旧生产代码下77组/1717断言,3组目标兼容断言失败;不是编译失败或apphost误用。原始B2-red-*全部保留。
- 绿测:77组全部通过,1783断言;B2-green-*记录精确DLL hash/实际时间/run。未重跑无关收费玩法或奖励单元凑覆盖。
## T1~T8验收
| 项 | 实际结果与证据 |
|---|---|
| T1 M15 | 缺键/JSON null/对象旗标区别、含空白Raw逐UTF8字节保留、模块外键保留、输入不变/Raw拷贝独立;非法组合及C# null/字符串null/非JSON空白/注释/多token/注入等拒绝;共用Json("null")仍拒绝。B2-green-result.json |
| T2 M22 | null/[]/非空列表与14字段保持、缺属性及错误元素拒绝、null不绕PackType、DTO非空深复制、Missing仍空集合。B2-green-result.json |
| T3 实际挑战对象 | 原M15-M26-M27固定case,63断言成功;fresh与已存在对象经Reset到空态,state0/ShouldOpen=false/null序列化,M26/M27回归;不启动Init或活动场景。B3-unity-activities.json |
| T4 实际礼包对象 | 原M21-M22-M24,146断言成功、restored=true、formalRequests=0;顺序替换null/[]/[7,9]、Snapshot字段比对、null比较不漏其他字段、实际数据序列化、集合拷贝与重置。B3-unity-bargain.json |
| T5 离线全量 | 77组/1783断言全部通过;包括原严格语法/其他Schema/事务与生命周期回归。B2-green-build.log、B2-green-tests.log |
| T6 完整事务与样本 | 原B2-20成功83断言、31真实模块/两epoch、sameObjects/restored=true;原固定Inspect下101 55键Valid/0诊断、102 45键Valid/0诊断,无第三问题。B3-unity-full-load.json、B3-source-inspection.json |
| T7 编译 | Editor Success=true、ErrorCount=0、WarningCount=272(原始警告保留,不称零警告);SERVICE_HOST exit0、0错误/2警告,警告指向未改的UserService及Stubs。B3-unity-compile.json、B3-servicehost-* |
| T8 边界 | 4生产+3测试白名单,未发现范围外受审文件变化(共享状态更新除外);883项cfgData/Tables不变,原备份hash不变,Unity末次错误日志0,无Play/run/permit,11 claimed保留。B3-verification-summary.json、B3-final-unity-errors.json、B4-scope-verification.json |
B2-20输出auth=2/gets=2是隔离模拟事务的认证/读取计数,不是正式网络请求;本轮未执行s101/PlayFab数据接口操作。Inspect仍是sourceOnly/syntheticDescriptor/usesLocalUtc,本地实际UTC窗口见B3-source-inspection-window.json。没有伪称真实认证、服务器时间或所有玩家兼容率。
## 样本与限制
| 样本 | 原SHA256 | 本次 |
|---|---|---|
| PF101,55键 | E6EB3E656B62A3535FDB8F0DC517321A4C3E32F3A8D95385A2B7D5C0AA8222EF | 两类目标拒绝消失,Valid/0 |
| PF102,45键 | 827F36084648449CEFAFB96B3CE62093DF70B8A87E33A526DA5B03A9C135A377 | 保持Valid/0,未重新导入 |
Valid仅指现有Schema范围和当前冻结配置下的解码。26种模块外键、5种未定位键、真实身份/权威源/并发与恢复政策、未验证挑战入口和热换号均保持既有边界。M15 null安全只涵盖获审受控加载前态,不推导任意场景可达安全。
当前4C2及P2-α-A保持关闭;本次B4交付之后交B5独立实施复核,主会话核hash逐条裁决后才关闭两类空态兼容单元。整体P2-α/G4与正式迁移政策没有关闭。未打包。

View File

@@ -0,0 +1,42 @@
# P2-α-B实施复核裁决与关闭
2026-09-28,主会话。**接受独立实施复核,正式关闭两类历史空态的加载表示兼容单元P2-α-B;无需再复审该已关闭单元。** 不是整体P2-α/G4关闭,也不是101导入或真实启用批准。
## 修改共享状态前的基线核验
- implementation-review-hashes.json:1334/1334项SHA256及长度全部一致。
- 清单SHA256:9AAAFD7AC85925EA74B57115152BAD6BBFE45C3377C8F4801D3053FB45E2F305。
- 独立报告SHA256:44895A64319BBB3CFF44AFD9F29AC6277043CA4B9AB307AF98634235E0BDEEAC。
- 原绿测DLL与当前DLL均为88CC86E394D6CDDFDC634936D2639C203C5FC5E7D32D19B79BCE10E5D70C3ED5、495616字节。
- 核验落盘B5-closure-baseline-verification.json。原受审清单、报告和B1~B4证据保持原文;之后入口/状态/TODO/日志为关闭增量,不冒充旧受审版本。
## 八项逐条裁决
| 项 | 主会话裁决 |
|---|---|
| Q1 白名单与diff | 接受。恰为4生产+3测试已有文件,生产18增/5删,未顺带重构或改玩法 |
| Q2 M15 | 接受。四字符Trim、原文严格校验、三态内部DTO、非法组合、Raw字节与Replace闭合,共用helper保持 |
| Q3 M22 | 接受。仅selections显式null可接受,缺属性仍拒绝,14字段/PackType/错误分类/深复制保持 |
| Q4 测试比较器 | 接受。Bargain专用分支正确,其他字段比较及通用SameList不变;固定私有字段读取只用于验证,无新可调用入口 |
| Q5 验证 | 接受原作者红77/3→绿77/0、1783断言、Unity63/146/83和双编译0错误;独立复跑作为补充,过程偏差见下节 |
| Q6 原样本 | 接受。101 55键/102 45键均Valid/0,原值hash及883配置指纹保持;不能推为全玩法/全用户兼容 |
| Q7 运行边界 | 接受原B3证据。无Play/许可/正式数据接口/导入/打包;本关闭轮仅文件读取/hash,不冒称又运行了一次Unity验证 |
| Q8 关闭 | 接受。关闭两类空态加载表示兼容;101未真实启用,整体迁移条件继续开放 |
## P3-1裁决:已覆盖,不新增测试
ActivitySchemaTests.cs:66已显式包含`\vnull\v`、`\fnull\f`、`\u00a0null\u00a0`,并含U+0085、U+FEFF及其他非法边界。:68~70对每项要求返回null且M15 Malformed计数为1。原B2-green-result.json中`M15-explicit-null-raw-and-strict-boundaries`通过43断言;M15严格对象组通过48断言。
因此采纳审查者“裁决时确认即可”的建议,认定配套覆盖已完成;无需再改代码、补重复测试或重跑。
## 复核过程偏差与证据口径
独立报告写“先构建后dotnet DLL执行”,而B5提示词要求先核锁定DLL、不得重建。该过程文字不符合原约定,不能表述成“完全按禁止重建流程完成”。主会话目前可核事实为1334项一致且当前DLL仍与作者交付SHA256/字节完全一致;并不能从当前相同hash反推审查者操作顺序。
本关闭以作者已封存红绿/Unity证据、当前源码及DLL一致性、独立源码审查接受为依据;报告中的独立复跑是补充证据,不替代作者原始验证。保留偏差,不要求重复构建/测试来消除文字差异。后续复核应先核DLL后直接运行,若确需重建须单独说明并保留前后hash,不能称为原约定只读复跑。
## 关闭后
下一步在主会话侧:整理整体P2-α剩余迁移条件、未覆盖业务与最小后续验证范围。见[剩余事项与下一步清单](../P2-alpha剩余迁移条件与下一步清单-2026-09-28.md)。已经通过的R1/4C1/4C2/A/B不重复开发或消费补样;新代码仍须完整方案审查。
固定http://s101.local:18080/;Unity CLI Loop,不用UnitySkills、不打包。

View File

@@ -0,0 +1,18 @@
# P2-α-B两类历史空态兼容:独立实施复核
请独立核对本目录implementation-review-hashes.json的SHA256/字节,阅读主方案修订1、原方案与补审报告、两次裁决、B4交付、7文件before/diff及原始红绿/Unity输出。原Json前置遗漏已补审批准,不必再重复方案审查;核实际代码是否精确落实即可。
本轮不操作Unity/Play/许可、不访问正式数据或写存档,不改共享文档或源码。只落盘报告:`CodexOutput/upserver/P2-alpha-B两类历史空态兼容实施独立复核-2026-09-28.md`。若需要独立离线复跑,只运行明确的KVGameLoadTests.dll,先核B2-green-artifact.json中的hash,不能直接启动共享apphost,也不能重建或改实现来获得通过;如DLL已变,登记而不混用证据。
请逐条回答:
1. 4生产+3测试文件/成员白名单及diff是否符合,18新增/5删除生产行数是否真实,是否引入无关业务逻辑变化。
2. M15仅四种JSON空白识别、StrictJsonSyntax验证原文、其他输入原Object(Json)、三态DTO/非法组合/XML注释/Replace/Raw字节是否闭合;共用Json及StrictJsonSyntax是否确实不变。
3. M22仅嵌套selections显式null放宽,缺属性及其他14字段/PackType严格保持,null/[]区别及DTO/Apply/Snapshot/序列化/深复制是否充分。
4. 测试比较器只改Bargain专用分支是否正确;固定case私有字段只读是否限于验证实际保存值、没有新增任意反射入口;其他字段不等/null与[]不等是否入测。
5. 红测77组3失败→绿测77组0失败/1783断言是否真实,精确DLL而非apphost,首败是否保留;Unity63/146/83及双编译0错误是否对应最终源hash。
6. 101原55键/102原45键的源hash、当前Valid/0、883配置指纹、UTC窗口及合成身份说明是否一致;不把Decode Valid或31节点当作全用户/全玩法/模块外键验证完成。
7. Editor末态、无许可/11 claimed/错误0及无正式数据接口/无导入/无Play/不打包是否守住。B2-20的auth/gets须按模拟请求而非正式请求解释。
8. 是否接受并允许关闭本两类空态兼容单元;如存在问题,给P1/P2/P3精确位置及最小修正,不扩展为真实账号试错、全量迁移或活动重构。
输出受审hash结果、八项逐条结论、发现清单及接受/有条件接受/退回。主会话负责之后裁决、关闭和下一步,不由复核侧修改入口/TODO或代替产品迁移政策确认。

View File

@@ -0,0 +1,148 @@
# M15逐引用前置状态锚点
沿用已复核的142处引用;近邻声明由文本定位,不能当作完整C#方法归属或控制流支配证明。实际读取语句与原台账逐行对应。全局门锚点见主方案§3,未证明的入口保留限制,不以补文档为由新增业务保护。
| 引用位置 | 分类 | 近邻声明锚点 | 近邻条件锚点 | 前置状态/限制 |
|---|---|---|---|---|
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:63 | property-declaration | 41 | 64: internal bool IsInactiveForKVValidation => !IsInit && CurFishingEventData==null && eventChallengeMain==null &&<br>65: eventChallengeConfig==null && challengeMapData==null && _stageDropList.Count==0 && _eliminateListScore.Count==0; | 初始引用null;旧缺键未调用Init不等于清除已存在旧对象 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:72 | assignment | 70 | | 写入/Reset/反序列化/实例创建点;须结合对应调用前态,见语义报告 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:79 | assignment | 76 | 78: if(state==null) throw new ArgumentNullException(nameof(state)); | 写入/Reset/反序列化/实例创建点;须结合对应调用前态,见语义报告 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:112 | requires-active-context | 110 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:115 | requires-active-context | 110 | 115: if (fishingChallengeData.FirstOpen) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:117 | requires-active-context | 110 | 115: if (fishingChallengeData.FirstOpen) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:143 | null-observation | 139 | 143: if (fishingChallengeData == null) | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:145 | requires-active-context | 139 | 143: if (fishingChallengeData == null) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:153 | null-observation | 139 | 151: if (!IsTimeAllow())<br>153: InVisibleReason = $"IsTimeAllow Failed {ZZTimeHelper.UtcNow()} {fishingChallengeData?.StartTime} {fishingChallengeData?.EndTime}"; | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:160 | null-observation | 139 | 158: if (IsClearAllLevel())<br>160: InVisibleReason = $"IsClearAllLevel Failed {ZZTimeHelper.UtcNow()} {fishingChallengeData?.RankTierLevel} {TopRankTier}"; | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:173 | requires-active-context | 169 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:174 | requires-active-context | 169 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:186 | assignment | 184 | | 写入/Reset/反序列化/实例创建点;须结合对应调用前态,见语义报告 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:202 | requires-active-context | 200 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:208 | requires-active-context | 205 | 208: if (fishingChallengeData.RankTierLevel <= TopRankTier) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:218 | assignment | 215 | | 写入/Reset/反序列化/实例创建点;须结合对应调用前态,见语义报告 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:227 | requires-active-context | 221 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:228 | requires-active-context | 221 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:229 | requires-active-context | 221 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:230 | requires-active-context | 221 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:231 | requires-active-context | 221 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:232 | requires-active-context | 221 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:233 | requires-active-context | 221 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:234 | requires-active-context | 221 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:251 | null-observation | 244 | 251: if (CurFishingEventData != null && fishingChallengeData != null && CurFishingEventData.ID != fishingChallengeData.EventId) | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:256 | null-observation | 244 | 256: if (fishingChallengeData == null)<br>258: if (CurFishingEventData == null) // 不存在Event | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:262 | assignment | 244 | | 写入/Reset/反序列化/实例创建点;须结合对应调用前态,见语义报告 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:281 | null-observation | 278 | 281: if (fishingChallengeData != null)<br>283: if (!IsTimeAllow()) | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:286 | requires-active-context | 278 | 286: if (fishingChallengeData.Finished == 0) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:324 | requires-active-context | 315 | 322: else if (status == 1) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:327 | requires-active-context | 315 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:332 | requires-active-context | 315 | 330: else if (status == 2) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:333 | comment | 315 | | 注释不执行,不能作为读取入口 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:352 | requires-active-context | 341 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:355 | requires-active-context | 341 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:363 | requires-active-context | 341 | 361: if (endIndex >= GetFullStep() - 1) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:365 | requires-active-context | 341 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:368 | requires-active-context | 341 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:382 | requires-active-context | 380 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:388 | requires-active-context | 386 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:398 | requires-active-context | 396 | 398: if (fishingChallengeData.Score == 0) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:404 | requires-active-context | 396 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:406 | requires-active-context | 396 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:436 | requires-active-context | 432 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:463 | requires-active-context | 455 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:555 | requires-active-context | 553 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:556 | requires-active-context | 553 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:557 | requires-active-context | 553 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:558 | comment | 553 | | 注释不执行,不能作为读取入口 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:559 | requires-active-context | 553 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:566 | comment | 562 | | 注释不执行,不能作为读取入口 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:572 | null-observation | 569 | 572: if (fishingChallengeData == null &#124;&#124; eventChallengeConfig == null &#124;&#124; eventChallengeMain == null) | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:574 | requires-active-context | 569 | 572: if (fishingChallengeData == null &#124;&#124; eventChallengeConfig == null &#124;&#124; eventChallengeMain == null) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:592 | requires-active-context | 588 | 590: if (isSuccess) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:593 | requires-active-context | 588 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:605 | null-observation | 601 | 605: if (fishingChallengeData == null) | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:618 | requires-active-context | 601 | 619: if (qualityScore > 0) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:621 | requires-active-context | 601 | 619: if (qualityScore > 0) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:622 | comment | 601 | | 注释不执行,不能作为读取入口 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:628 | requires-active-context | 601 | 629: .AddContent("tiler", eventChallengeMain?.ID) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:630 | requires-active-context | 601 | 629: .AddContent("tiler", eventChallengeMain?.ID) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:662 | requires-active-context | 659 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:686 | requires-active-context | 678 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:710 | requires-active-context | 704 | 708: if (_stageDropList.Count > 0) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:712 | requires-active-context | 704 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:723 | requires-active-context | 721 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:729 | requires-active-context | 727 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:735 | requires-active-context | 733 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:746 | requires-active-context | 741 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:799 | requires-active-context | 741 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:800 | requires-active-context | 741 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:812 | requires-active-context | 741 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:817 | requires-active-context | 741 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:831 | requires-active-context | 821 | 829: if (rand < 0) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:844 | null-observation | 840 | 844: if (fishingChallengeData != null) | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:846 | requires-active-context | 840 | 844: if (fishingChallengeData != null) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:852 | requires-active-context | 840 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:853 | requires-active-context | 840 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:874 | assignment | 869 | 872: if (!isSuccess) | 写入/Reset/反序列化/实例创建点;须结合对应调用前态,见语义报告 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:902 | requires-active-context | 896 | 900: var tier = eventChallengeMain?.RankTier ?? -1; | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:915 | null-observation | 896 | 914: var finalRewardList = eventChallengeMain?.FinalReward?<br>915: .ElementAtOrDefault(fishingChallengeData?.index ?? -1) ?? -1;<br>916: var finalPlayer = eventChallengeMain?.FinalPlayer?<br>917: .ElementAtOrDefault(fishingChallengeData?.index ?? -1) ?? -1; | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:917 | null-observation | 896 | 915: .ElementAtOrDefault(fishingChallengeData?.index ?? -1) ?? -1;<br>916: var finalPlayer = eventChallengeMain?.FinalPlayer?<br>917: .ElementAtOrDefault(fishingChallengeData?.index ?? -1) ?? -1; | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:934 | null-observation | 896 | 933: var finalRewardList = eventChallengeMain?.FinalReward?<br>934: .ElementAtOrDefault(fishingChallengeData?.index ?? -1) ?? -1;<br>935: var finalPlayer = eventChallengeMain?.FinalPlayer?<br>936: .ElementAtOrDefault(fishingChallengeData?.index ?? -1) ?? -1; | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:936 | null-observation | 896 | 934: .ElementAtOrDefault(fishingChallengeData?.index ?? -1) ?? -1;<br>935: var finalPlayer = eventChallengeMain?.FinalPlayer?<br>936: .ElementAtOrDefault(fishingChallengeData?.index ?? -1) ?? -1; | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:950 | null-observation | 946 | 948: if (CurFishingEventData == null)<br>950: if (fishingChallengeData != null) | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:952 | requires-active-context | 946 | 950: if (fishingChallengeData != null) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:953 | requires-active-context | 946 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:963 | null-observation | 946 | 963: if (fishingChallengeData != null) | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:965 | requires-active-context | 946 | 963: if (fishingChallengeData != null) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:966 | requires-active-context | 946 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:975 | requires-active-context | 971 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:986 | requires-active-context | 971 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:1009 | requires-active-context | 1001 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:1034 | null-observation | 1028 | 1034: if (fishingChallengeData == null) | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:1044 | requires-active-context | 1028 | 1044: else if (fishingChallengeData.eliminateNumber == null &#124;&#124; fishingChallengeData.robotAccountList == null) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:1074 | null-observation | 1072 | 1074: if (fishingChallengeData != null) | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:1076 | requires-active-context | 1072 | 1074: if (fishingChallengeData != null) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:1084 | save-null-capable | 1082 | | null可序列化为原字符串null;证明旧代码能产出本样本,不意味着允许删除键 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:1089 | null-observation | 1087 | 1089: if (fishingChallengeData != null)<br>1091: return fishingChallengeData?.EventId + "_LL_FIRST_OPEN"; | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:1091 | null-observation | 1087 | 1089: if (fishingChallengeData != null)<br>1091: return fishingChallengeData?.EventId + "_LL_FIRST_OPEN"; | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:1098 | requires-active-context | 1096 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs:1099 | requires-active-context | 1096 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/Services/UserService.KVFishingReward.cs:127 | null-observation | 58 | 127: if(challenge.fishingChallengeData!=null && challenge.ChallengeState()!=0 && challenge.ChallengeState()!=3 && challenge.IsTimeAllow())<br>129: if(player._fishingFrequency<3 &#124;&#124; !player.IsOpenMagnification &#124;&#124; !fish.IsOpenPiercing) | 同为null时观察分支一致;不能覆盖同方法内其他直接解引用 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/Road/ChallengeMatchSceneRoadUI.cs:59 | requires-active-context | 52 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/Road/ChallengeMatchSceneRoad.cs:81 | alias-read | 66 | | RoadUI缓存别名_thisEventData;下一行82读取step,需要非null活动前置,已另追踪该别名 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/Road/ChallengeMatchPlayers.cs:149 | comment | 132 | 149: // if (i >= _fishingChallengeManager.fishingChallengeData.robotAccountList.Count) | 注释不执行,不能作为读取入口 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/Road/ChallengeMatchPlayers.cs:151 | comment | 132 | 149: // if (i >= _fishingChallengeManager.fishingChallengeData.robotAccountList.Count)<br>152: // if (robots.TryGetValue(_fishingChallengeManager.fishingChallengeData.robotAccountList[idx], | 注释不执行,不能作为读取入口 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/Road/ChallengeMatchPlayers.cs:152 | comment | 132 | 152: // if (robots.TryGetValue(_fishingChallengeManager.fishingChallengeData.robotAccountList[idx], | 注释不执行,不能作为读取入口 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/Road/ChallengeMatchPlayers.cs:160 | requires-active-context | 132 | 160: if (robots.TryGetValue(_fishingChallengeManager.fishingChallengeData.robotAccountList[i], | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/Road/ChallengeMatchPlayers.cs:229 | requires-active-context | 221 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/ChallengeMatchSceneUI.cs:139 | requires-active-context | 134 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/ChallengeMatchSceneUI.cs:175 | requires-active-context | 153 | 175: if (robots.TryGetValue(_fishingChallengeManager.fishingChallengeData.robotAccountList[i], out cfg.Robot robot)) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/ChallengeMatchSceneUI.cs:199 | comment | 153 | | 注释不执行,不能作为读取入口 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/ChallengeMatchSceneUI.cs:203 | comment | 153 | | 注释不执行,不能作为读取入口 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/ChallengeMatchSceneUI.cs:320 | requires-active-context | 310 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/ChallengeMatchSceneUI.cs:442 | comment | 310 | | 注释不执行,不能作为读取入口 |
| Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs:305 | validation-only | 150 | | 既有验证读取;不属于线上入口 |
| Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs:328 | validation-only | 150 | 328: Check(club.SnapshotForKVValidation().DonateCount==7 && challenge.fishingChallengeData!=null && chains.OfferChainsData!=null && disco.OfferDiscoPartyData!=null && partner.GatherData!=null && cube.EventData!=null);<br>330: Check(potion.PotionData!=null && pinball.PinballData!=null && scratch.ScratchTicketData!=null && progress.SnapshotForKVValidation().IsPresent && migration.SnapshotForKVValidation().IsPresent && pinataTarget.PurchasedCount==9 && digging.DataModel!=null); | 既有验证读取;不属于线上入口 |
| Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs:344 | validation-only | 150 | 343: Check(club.SnapshotForKVValidation().SignTime==null && club.SnapshotForKVValidation().DonateCount==0 && club.SnapshotForKVValidation().OpenRed);<br>344: Check(challenge.fishingChallengeData==null && challenge.IsInactiveForKVValidation && chains.OfferChainsData==null && chains.IsInactiveForKVValidation && disco.OfferDiscoPartyData==null && disco.FreeTime==1 && disco.IsInactiveForKVValidation);<br>345: Check(partner.GatherData==null && partner.RuntimeEmptyForKVValidation && !ReferenceEquals(oldPartner,partner.PartnerInfoManager) && !ReferenceEquals(oldRobot,partner.RobotInvitationSystem));<br>346: Check(cube.EventData==null && cube.IsInactiveForKVValidation && eventPack.SnapshotForKVValidation().PurchaseCount==0 && !piggy.KVLoadPresent && !bargain.KVLoadPresent && !shooting.KVLoadPresent && foot.CompleteFootPrintForKVValidation==0); | 既有验证读取;不属于线上入口 |
| Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs:695 | validation-only | 150 | 693: Check(state!=null && state.IsPresent); | 既有验证读取;不属于线上入口 |
| Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs:712 | validation-only | 150 | | 既有验证读取;不属于线上入口 |
| Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs:714 | validation-only | 150 | | 既有验证读取;不属于线上入口 |
| Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs:716 | validation-only | 150 | 718: Check(challenge.fishingChallengeData==null && challenge.IsInactiveForKVValidation); | 既有验证读取;不属于线上入口 |
| Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs:718 | validation-only | 150 | 718: Check(challenge.fishingChallengeData==null && challenge.IsInactiveForKVValidation); | 既有验证读取;不属于线上入口 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/ChallengeMatchScene1.cs:113 | requires-active-context | 100 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/ChallengeMatchScene1.cs:114 | comment | 100 | | 注释不执行,不能作为读取入口 |
| Client/Assets/Scripts/UI/Challenge/EventChallengeRewardPopupPanel.cs:78 | requires-active-context | 65 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Challenge/EventChallengeRewardPopupPanel.cs:92 | requires-active-context | 84 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/GampPlay/ChallengeMatch/ChallengeMatchScene.cs:46 | requires-active-context | 29 | 47: if (step < 0) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Challenge/EventChallengePanel.cs:170 | requires-active-context | 167 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Challenge/EventChallengePanel.cs:232 | requires-active-context | 227 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Challenge/EventChallengePanel.cs:233 | requires-active-context | 227 | 235: // if (oldScore == newScore && newScore = ) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Challenge/EventChallengeMatchingLoadingPanel.cs:143 | requires-active-context | 114 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Home/HomeBtnChallenge.cs:98 | requires-active-context | 67 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Home/HomeBtnChallenge.cs:113 | requires-active-context | 67 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Home/HomeBtnChallenge.cs:192 | requires-active-context | 185 | 192: else if (_fishingChallengeManager.PreScore != _fishingChallengeManager.fishingChallengeData.Score) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Home/HomeBtnChallenge.cs:211 | requires-active-context | 197 | 212: if ((float)(_fishingChallengeManager.fishingChallengeData.Score - _fishingChallengeManager.PreScore) / newScore > 0.05f) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Home/HomeBtnChallenge.cs:212 | requires-active-context | 197 | 212: if ((float)(_fishingChallengeManager.fishingChallengeData.Score - _fishingChallengeManager.PreScore) / newScore > 0.05f) | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Home/HomeBtnChallenge.cs:218 | requires-active-context | 197 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Home/HomeBtnChallenge.cs:219 | requires-active-context | 197 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Home/HomeBtnChallenge.cs:258 | requires-active-context | 232 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |
| Client/Assets/Scripts/UI/Home/HomeBtnChallenge.cs:259 | requires-active-context | 232 | | 若两种输入在fresh/Reset后均物化null,直接解引用行为相同:无活动前置时都可能抛异常;不能称空值在所有入口安全。 |

View File

@@ -0,0 +1,98 @@
{
"phase": "P2-alpha-B-closed-document-delta",
"createdUtc": "2026-09-28T06:52:42.8404064Z",
"count": 18,
"note": "原实施1334项清单不重写,关闭前全量核验见B5记录,5份共享文档为此关闭增量",
"items": [
{
"path": "Client/Assets/Scripts/DataCenter/BargainPackData.cs",
"sha256": "773B36C65A5690AE5882258FC111EAFEF868090D359C9C6996FF9146EB90B3C9",
"bytes": 12362
},
{
"path": "Client/Assets/Scripts/DataCenter/FishingChallengeManager.cs",
"sha256": "C03389252134497CC286440A938F893B8A8E2470EC3527A5DB35FE87ACA00666",
"bytes": 37862
},
{
"path": "Client/Assets/Scripts/Services/PlayerData/PlayerKVDecodedSave.cs",
"sha256": "35859243346DFB5A7E1ABC504CBCDDE3818F0793233FCEC8435A74C84D721C03",
"bytes": 57059
},
{
"path": "Client/Assets/Scripts/Services/PlayerData/PlayerKVEditorValidationHost.cs",
"sha256": "EAFDF80AC93B4726FDDCAEB359C6A1E0B0F26A3154FAEC8F8A48EA1840C94FE6",
"bytes": 140805
},
{
"path": "Client/Assets/Scripts/Services/PlayerData/PlayerKVSaveSchema.cs",
"sha256": "FBDB253D3415E35D7D30F867DA057A84E91EAA7DE2FE74094707C517CB8B51A1",
"bytes": 76183
},
{
"path": "CodexOutput/upserver/阶段4B3当前TodoItem-2026-09-24.json",
"sha256": "254F9A458A4AB0E6EEB7BE452D42C81F0C6507EAB8D1225BA735B9B4992E1F58",
"bytes": 18674
},
{
"path": "CodexOutput/upserver/接手入口.md",
"sha256": "2B76435C410795899FFFFAD583E4FBACDB1EC0E4BB8FA104226CC8D4B416A2D7",
"bytes": 64712
},
{
"path": "CodexOutput/upserver/开发日志.md",
"sha256": "1CFE22713110B482743BFA26C246770CEF4FBC07B61F94C5493311AD90ACD210",
"bytes": 189790
},
{
"path": "CodexOutput/upserver/开发状态.md",
"sha256": "0D7000FA72196F6BA9C8F999D89C961C3883B39E784640B33182D1489ABCC1AF",
"bytes": 68420
},
{
"path": "CodexOutput/upserver/KVGameLoadTests/ActivitySchemaTests.cs",
"sha256": "5796151FD8927DDFB76CFBB146AB02BC9F61BD399E17CEC839EEB3945F1F9E4E",
"bytes": 10277
},
{
"path": "CodexOutput/upserver/KVGameLoadTests/D3SchemaTests.cs",
"sha256": "2DD579BE56E9B7D5BF7434C0507ED553FC0536E0A1A1B7309736BF633496451E",
"bytes": 16882
},
{
"path": "CodexOutput/upserver/P2-alpha-B两类历史空态兼容实施独立复核-2026-09-28.md",
"sha256": "44895A64319BBB3CFF44AFD9F29AC6277043CA4B9AB307AF98634235E0BDEEAC",
"bytes": 4166
},
{
"path": "CodexOutput/upserver/P2-alpha剩余迁移条件与下一步清单-2026-09-28.md",
"sha256": "0B8D9E460C4F21B61F8E1DCEA878142EA93BC3E1D1B286641F9EE994C7967FE2",
"bytes": 4518
},
{
"path": "CodexOutput/upserver/P2AlphaNullCompatibility/B5-实施复核裁决与关闭.md",
"sha256": "C4AA2C3B239B0CA753CB1C422ED487973343544C9F07FDD0E9BCB9BAAF80DC6D",
"bytes": 3745
},
{
"path": "CodexOutput/upserver/P2AlphaNullCompatibility/B5-closure-baseline-verification.json",
"sha256": "3FC2A51F3AB076E5B93E8517645FC23AA0EA40FE762F157BB775B7A56E28CAC3",
"bytes": 542
},
{
"path": "CodexOutput/upserver/P2AlphaNullCompatibility/B5-closure-scope-verification.json",
"sha256": "0CD2E5B6638E8B3A9DE871C4724D730DCE0DF5A96285303E595ABB6F04DE018C",
"bytes": 457
},
{
"path": "CodexOutput/upserver/P2AlphaNullCompatibility/implementation-review-hashes.json",
"sha256": "9AAAFD7AC85925EA74B57115152BAD6BBFE45C3377C8F4801D3053FB45E2F305",
"bytes": 253557
},
{
"path": "CodexOutput/upserver/TODO.md",
"sha256": "C918495D7A814298A00B5368A9F31CDFF2F94BC7E4BF762AD685A33CF8C8EA3D",
"bytes": 72331
}
]
}

View File

@@ -0,0 +1,6 @@
{
"checkedUtc": "2026-09-28T04:06:52.0882831Z",
"checked": 1277,
"mismatches": [],
"manifestSHA256": "F9DB3DE824F24CA53467A0DB62FE07B6455C4655890C903699CD2A13D51D39EF"
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
{
"checkedUtc": "2026-09-28T06:32:17.2529415Z",
"checked": 1334,
"mismatches": [],
"manifestSHA256": "9AAAFD7AC85925EA74B57115152BAD6BBFE45C3377C8F4801D3053FB45E2F305"
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
{
"checkedUtc": "2026-09-28T06:19:51.7090487Z",
"checked": 1301,
"mismatches": [],
"manifestSHA256": "30EDAE97606F62148D6A97C482C30FFB360E29B604A0BD521921CFDF13EF23E7"
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
# P2-α-B修订1差异补审提示词
请独立审查已通过方案的一处实际不可达分支修订。先核`revision1-review-hashes.json`;阅读《B2-解析前置遗漏与修订1补审.md》和主方案修订1§3.1/T1,参照`B2-original-reviewed-plan.md`。不要重启/操作Unity、联网、写存档或修改共享文档/源码。
原方案和上一轮Q2假定共用Json可返回根Null,实际上PlayerKVSaveSchema:163直接拒绝。主会话只完成2离线测试文件红测,4生产文件及Editor host尚未修改。红测77组中3组失败都是目标兼容断言;原始证据在B2-red-*。
请核查:
1. 原阻塞是否成立,现有StrictJsonSyntax对null字面量、四种JSON空白和全量耗尽的行为是否支持修订1。
2. DecodeChallenge内部先识别ASCII JSON空白包裹的null、验证原文再返回ExplicitNull,其他输入仍Object(Json(text)),是否满足原三态/严格负例/Raw字节合同;无需改共用helper或增加解析器。
3. 新增非JSON空白、注释、额外token、尾逗号、注入字符串及原Json(null文本)仍拒绝的断言是否充分;C# null/字符串"null"/对象字段null仍与根null严格分离。
4. 白名单仍4生产+3测试,现有2文件红测改动是否仅落实已审方案;能否按修订1恢复B2~B4。M22嵌套null无需改变原方案的判断是否准确。
输出“接受/有条件接受/退回”、受审hash核验、逐项结论及精确最小问题,落盘`CodexOutput/upserver/P2-alpha-B修订1解析前置独立补审-2026-09-28.md`。报告后由主会话裁决,不替主会话编码或更新入口。不重复审已关闭P2-α-A,也不要求真实账号写入试错。

View File

@@ -0,0 +1,24 @@
# 定位尝试与102分叉时间线
2026-09-28;补齐P2-α-A独立执行复核的P3-2/P3-3。只引用已有本地证据,没有重新请求存档。
## 五种键的已有定位尝试
[原元数据](../P2AlphaAudit/A3-unlocated-key-search-meta.json)和[原空结果](../P2AlphaAudit/A3-unlocated-key-search.txt)记录范围`Client/Assets`+`Client/LocalPackages`中的`*.cs`,`rg --no-ignore`,退出码1,以下文本均无匹配:
`EventLuckyTowerData|EventOfferDevourFishData|EventTransitionTokenDic|Leadboard_LastContinent|SeasonStampData`
上述各项都是不含正则特殊字符的键名;用各项字面量或该alternation表达查询词集合。此处复述已封存搜索的范围/模式与结果,不冒充保存了原shell完整命令。不覆盖Library/PackageCache、外部仓库、资源事件或动态生成键;无文本命中不能推断废弃。新兼容方案完全保留这五键与值,无删除/迁移操作。
## 102的单一实例证据链
| 时点 | 已有证据 | 对权威源的意义 |
|---|---|---|
| 2026-09-24 04:18 UTC批次 | [PF102原45键备份](../KVGameplayLoopTests/pf-source-backups/20260924T041802Z/102.json),实际采样at以文件为准 | 旧PF当时样本,不能代表后来的KV |
| 2026-09-24 06:14 UTC | [一次导入执行日志](../KVGameplayLoopTests/102-import-execution-approved.log)、[导入前](../KVGameplayLoopTests/imports/20260924T061443Z/before.json)、[读回](../KVGameplayLoopTests/imports/20260924T061443Z/after.json)、[逐值核验](../KVGameplayLoopTests/imports/20260924T061443Z/verification.json) | 空目标检查后一次Set45,读回与原字符串一致;不是可重复运行授权 |
| 随后的R1/4C1 | [4C1关闭裁决](../阶段4C1实施复核裁决与关闭-2026-09-24.md)Q8:一次403→393、累计消耗102→112,恢复45键一致;R1装备等其他变化见既有记录 | KV已产生新的业务状态;这些数字属于4C1自身时间窗,不与后面窗口强行拼接 |
| 2026-09-24 4C2第三次 | [第三次记录](../阶段4C2第三次真实尝试与奖励确认记录-2026-09-24.md) | 已有收鱼/奖励进度及当时UI缺陷,不得用旧PF回退 |
| 2026-09-28第四次与恢复 | [完整记录](../阶段4C2第四次真实闭环与新进程恢复记录-2026-09-28.md)、[关闭裁决](../阶段4C2实施复核裁决与关闭-2026-09-28.md) | 一次消费10,鱼10223奖励2516,用户签到16200另计;新进程恢复确认 |
| 2026-09-28 02:55:17.4174851Z | [最终45键备份](../KVGameplayLoopTests/backups/20260928T025514Z/102.json):金币234004、体力383、鱼10223数量10/重量8.74 | 这是当前文档最后的观察值,不是未来永远有效的写前基线;本轮不远端刷新也不覆盖 |
后续迁移政策中“目标KV已存在后续进度”以此为具体反例:旧PF仍可用作解码样本,但无法覆盖KV当前权威进度。跨窗口差异可能包含不同次运行/用户操作,不用总差额臆造单次奖励。未来真实写入仍需当时权威源、身份、最新备份及停写窗口方案。

View File

@@ -0,0 +1,20 @@
# 101两类历史空态最小兼容:独立方案审查提示词
> 历史原轮提示词。本轮方案已通过并开始实施,发现共用Json拒绝根null的解析前置遗漏;当前使用《修订1独立补审提示词.md》及revision1-review-hashes.json,只补审该差异。原报告与原受审清单保留。
请作为独立方案审查侧,阅读本目录《101两类历史空态最小兼容方案-待评审.md》、两个附件及`design-review-hashes.json`,先重算全部受审hash/字节。前期P2-α-A已由主会话核1232项并裁决关闭,不重复要求复核只读盘点。
仅只读源码和本地证据,不修改生产/测试/共享入口/TODO,不启动Unity、Play、许可或正式接口;仅落盘报告:`CodexOutput/upserver/P2-alpha-B两类历史空态兼容方案独立审查-2026-09-28.md`。主会话在报告后负责核hash、逐条裁决及实施。
请逐条回答:
1. 四个生产文件+三个已有测试文件是否足够小,是否存在遗漏的强制消费者;禁止为了可空两字段引入通用迁移框架。
2. M15 IsPresent+IsExplicitNull及ExplicitNull工厂能否精确区分缺键/根JSON null/合法对象;原StrictJson保留、对象字段仍严格、Raw原值不变是否成立。可选构造参数及非法组合校验是否足够。
3. M15 Replace只物化null、现有Reset先行能否满足未激活合同;142行前置锚点是否诚实标出“近邻条件≠完整支配证明”,未把直接活动/场景入口误判安全。
4. M22仅selections属性的显式JSON null接受,缺属性继续拒绝,其他14字段/PackType/IsPresent保持,DTO/Apply/Snapshot的null与深复制是否完整;没有转[]、重建礼包或删键。
5. T1~T8是否覆盖预期红测、严格负例、真实对象Reset/Apply/null序列化、集合隔离、原B2-20、101/102固定Inspect、编译与无写入边界;Bargain专用Same对null的适配是否只影响测试且不漏比较其他字段,是否有无必要的回归或漏测。
6. 白名单、失败停点、第三种拒档不扩大、固定CLI/状态恢复与无Play/远端操作边界是否可执行;没有使用UnitySkills或新增入口。
7. 三条P3附件及102分叉时间线是否对应既有证据;政策和其他模块问题是否保持未决,没有把本方案当作101导入/正式启用授权。
8. 是否可批准B1~B4实施;给接受/有条件接受/退回,以及每条P1/P2/P3的精确源码/方案位置、最小修订要求。若不同意新增M15旗标,请同时说明如何不丢DTO/Raw存在性且减少改动,不能仅以样本变Valid替代语义论证。
保留独立性;不要替主会话编码、改共享文档或关闭本单元。报告结论之后仍由主会话明确实施裁决。