Files
ft/CodexOutput/upserver/P2AlphaImport101/D0-固定101历史档导入最小方案-待评审.md

125 lines
13 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.
# P2-α-D:固定101历史档导入最小方案
2026-09-28,版本1,主会话。**方案完成,待独立评审;未编码、未导入。** 用户已确认固定历史备份与101映射;本方案不再请求重复确认该决定。真实写入前的停写窗口尚未建立。
## 1. 目标、产品决定和不做的事
将用户指定的历史PF101样本作为可丢弃专用账号101的固定测试起点,原样写入空的s101目标,再完整读回核对。这是一次测试导入,不代表所有历史用户、活动业务或身份系统已完成迁移。
- PF来源:`KVGameplayLoopTests/pf-source-backups/20260924T041802Z/101.json`,title108EE8/customId101,55键,SHA256=`E6EB3E656B62A3535FDB8F0DC517321A4C3E32F3A8D95385A2B7D5C0AA8222EF`。
- 用户确认其映射至s101 playerId101,接受固定历史测试起点;不要求重新访问PF,也不把其称为当前PF最新数据。
- API固定`http://s101.local:18080/`;目标非空、来源或候选校验失败则停止,不覆盖、不合并、不生成空白档。
- 102及其导入claim保持原状;不登录102、不复用/修改Import102,不运行PF、玩法、批量迁移、热换号、打包或新交易/CAS框架。
- C的070339Z目标空快照仅为历史参考;其at是本地UTC,本地钟与服务器钟偏差未校验。实际写前必须新Get,不把历史快照当锁或服务时间。
- 不合格时只停止101本次导入,不改变旧PF入口、路由或全体用户产品策略。26模块外键等保持原值,不声称业务消费者已迁移。
## 2. 选型与改动白名单
| 方案 | 结论 |
|---|---|
| 单独固定101辅助入口,沿用既有客户端/解码器 | 采用,便于约束一次测试,游戏逻辑零改动 |
| 把Import102改成通用账号迁移器 | 不采用,扩大原已验收路径和参数范围 |
| 直接HTTP脚本导入,省略候选校验/claim/替身测试 | 不采用,无法验证写前拒绝与Unknown路径 |
唯一允许的源码变更(均在`CodexOutput/upserver/KVGameplayLoopTests/Preflight/`):
1. **Program.cs**:增加两个严格单参数分支`--verify-import-101`和`--import-approved-101`。保留现有分支内容/默认拒绝逻辑不变,不添加任意账号/地址/路径参数。
2. **新增Import101.cs**:固定入口及一次导入流程;复用S101AccountSession、PlayerKVClient、PlayerKVSaveSchema.Decode。配置快照构造沿用Import102现有取值方式,复制所需短函数,不为共享函数改Import102。
3. **新增Import101Tests.cs**:固定离线验证组及替身handler;不新建通用测试/迁移框架。
csproj已有本目录C#默认包含规则,不修改项目文件。Client下C#、cfgData、场景、原备份、旧导入器及claims禁止改动。新编译产物只进入本目录D1-bin及原Preflight obj;无Unity编译必要。
数据/证据输出仅本目录:已冻结`D0-source-values.json`(55键名/hash/UTF8长度,不含整档值);D1/D2离线证据;D3停写记录;唯一`import-101.claimed`;`runs/<UTC+随机唯一后缀>/`的账号描述、before、候选报告、Set结果、after和核对。不得覆盖旧文件。
## 3. 固定输入与本地门
固定生产入口不接收外部覆盖参数,检查顺序:
1. 确认固定工作目录/源路径/API/101常量;现有claim存在即拒绝,禁止删除旧claim或换目录重试。
2. 校验原文件完整hash,再用DateParseHandling.None、拒绝重复属性/尾随内容读取。source=PlayFab/titleId108EE8/customId101、非空playFabId必须符合冻结源;data只能是55项唯一键/字符串值。
3. 逐项与受审D0-source-values的Ordinal键集合、UTF8长度/hash匹配;源原值保持在内存,不重新序列化值内JSON。未知键同样写入,不能按31模块过滤。
4. 配置固定读取7项:tbglobalconfig、tbroddata、tbmapdata、tbeventpackmanager、tbfishingevent、tbfishdata、tbfishcard;以及既有cfg.ConditionType。固定清单`D0-config-hashes.json`,版本`import101-fixed-20260928-v1`;送审hash不符停止,不自动更新、补配置或转默认值。两个D0数据清单本身的hash须在实现中锁定并随实施复核核验,不允许通过同时修改清单绕过来源/配置门。
5. 新证据目录必须不存在。目录或本地证据持久化失败时,写前直接停止;无正式请求也要保留可用错误输出。
源元数据检查和逐值清单是误操作防护,不是秘密/签名校验。来源及测试映射的产品依据是本轮用户明确答复。
## 4. 登录、候选和一次写入顺序
### 4.1 登录与写前数据
只登录101一次;使用原会话Cookie/禁止重定向/既有超时。成功且NewlyCreated=false,PlayerId101;记录InternalId、CreateTimeUtc、ServerTimeUtc及本地采集时刻,不记录Cookie。PlayerId是客户端请求ID,不把它冒充服务端回显或正式跨系统身份绑定。
登录可能产生会话/更新元数据;newCreate后置拒绝不能撤销服务器可能已创建的账号。失败/newCreate停止,不Get或清理账号,不自动重试。
随后第一次全量Get,必须成功,将完整before原值持久化后要求0键。非空立即停止,无Set/删除/合并。此Get不提供CAS,安全边界依赖已确认的专用账号停写窗口;不宣称消除了外部并发竞争。
构造与Import102同类的候选:本次真实账号描述符、固定epoch=1/专用ownerId、原PF playFabId身份快照、channel none、固定cfg快照及**本次ServerTimeUtc**。只Decode,不Apply/Open真实游戏会话。完整保存verdict/diagnostics,再要求Valid;未通过则保留原档并停止。A/B样本Valid只作依据,不跳过本次候选。
### 4.2 claim与Set
以上所有写前证据保存成功后,用CreateNew、Flush(true)持久化固定`import-101.claimed`:账号/API、源hash、键数、证据目录、配置版本、开始UTC和已确认停写记录标识。claim冲突/写盘失败则零Set;若已留下部分claim也不得自动删除。claim持久化之后、调用Set之前即认定“本次尝试已消耗”,即使进程此时崩溃也不自动重跑。
只调用一次SetAsync,载荷为全部55键原字符串。没有Set重试、PF双写、Delete或补偿。进入调用后本地/网络异常都按“可能已写”处理;不能因没有set-result文件就判定没写。
### 4.3 写后观察与本地落盘失败
Set调用正常返回或抛异常后,只要进程仍可运行且无并发/身份等新异常,均尝试**一次**同会话全量Get观察;Set结果文件保存失败不得短路此观察。实现用明确的try/catch/finally路径:单独记录网络结果/异常与本地保存异常,再走观察;Get失败不重试,保留claim。
观察结果若可用,尽力以CreateNew保存完整after及55项逐值矩阵(新增/缺失/额外键、UTF8长度/hash)。写后磁盘失败只能说明证据不完整,不反推KV没写;将错误送stderr/非零退出,仍不再Set。若进程/系统崩溃,无法保证finally运行,留到人工只读观察,不宣称总能获得after。
成功必须同时满足:Set明确Success、after成功且恰55键、全部原值相同、必需证据均持久化、停写边界未被破坏。任何一项不满足即“需人工核查”,即使Unknown后读值相同也不自动标成功。随后不Play、不补发;claim永久保留。
正常路径上限:1次登录+2次Get+1次Set,应用层4调用;失败按实际已到达阶段计数,不硬编码总是4。PF/102/Delete=0。观察Get读取原账户状态,不触发业务保存。
## 5. 狭窄测试接缝与离线验证
测试必须跑同一导入核心,不复制一套流程。允许在Import101内部提供仅测试使用的参数:输入副本/证据临时目录、CookieContainer→HttpMessageHandler工厂、候选校验回调、证据保存回调。固定CLI运行入口全部绑定真实固定实现;CLI不得传入这些参数,测试入口不得创建真实HttpClientHandler或用null回退联网。
接缝只用于:拒绝源变更的临时副本、有限mock序列、强制候选无效、模拟before/claim/set-result/after保存失败。候选真实实现仍调用既有完整Decode;成功组至少一次使用真实55键及真实配置的隔离候选解码,其他组可替身返回verdict。mock仅允许101登录/Get/Set固定URL,额外请求、102/PF/Delete或不符合顺序立即失败。测试Cookie由替身构造,不访问网络。
| 验证组 | 必须断言 |
|---|---|
| 源/配置/清单不符 | 错源hash、条目遗漏/重复或值变更、配置hash不符均不登录/不Set;原样55键可通过本地门 |
| 登录拒绝 | HTTP/协议失败、newCreate=true;Get/Set均0,不自动清理 |
| 目标非空/Get失败 | Set=0,非空before保留;无额外登录或重试 |
| 候选无效 | Set=0、claim未创建、诊断保存;记录这是故障注入,非真实样本不兼容 |
| before/候选保存失败 | Set=0,不跨过持久化门 |
| claim既有/写失败 | Set=0,旧claim字节保持;失败留下的claim不删除 |
| 正常成功 | 登录1/Get2/Set1,完整载荷55项逐值相同(含两种历史null字符串/未知键),结果与证据齐全 |
| Set返回Unknown/抛异常 | 仍Get一次,Set始终1,结果不标成功;无Delete/补偿/重试 |
| Set结果落盘失败 | 仍Get一次、非零结束,after可落则保留,不能静默成功 |
| after失败/缺键/多键/值变/落盘失败 | 非成功、claim保留、Set1;所有故障不触发第二次Set |
| 再执行 | 已有claim在网络前拒绝,原claim/快照保持,不通过删除claim复测 |
边界测试使用临时目录并保留摘要,不改原备份/生产claim。无意义的全量历史回归不重跑;既有Program分支内容和Import102全文件hash须保持(Program仅两处分支新增)。如实现必须超出三文件范围或增设通用服务,先停并修订方案。
## 6. 检查点与交接
- **D0**:本完整方案、产品决定、55项源指纹、依赖hash及自审送独立方案评审;主会话逐条裁决前不写C#。
- **D1**:通过后按三文件白名单实现。显式`dotnet build .../Preflight.csproj --no-restore -o CodexOutput/upserver/P2AlphaImport101/D1-bin`;不得隐式restore/运行正式分支。保存输入/产物hash、diff、构建结果。
- **D2**:只运行锁定DLL的`--verify-import-101`,完成上表替身验证;记录断言/请求计数及失败证据、确认源码范围。**交独立实施复核,主会话裁决前不运行正式导入分支。** 本步骤不要求用户开启停写窗口。
- **D3**:实施复核通过后,主会话向用户明确实际执行窗口:从确认101所有客户端/设备停用开始,至本次执行结束并报告;不声称已由当前来源确认建立。按Unity CLI Loop核本地空闲/无permit并冻结全部claimed;若编辑器已关闭只读进程与文件,不强启/强退。确认停写后记录D3窗口文件,仅运行锁定DLL的`--import-approved-101`一次。
- **D4**:前后状态/claimed核对、原源不变、真实账号/配置/候选、before/Set/after/对账和异常口径全部交付。新import-101.claimed为唯一预期新增,既有11 gameplay claimed不变;无Play。交独立执行复核后主会话逐条裁决关闭,只关闭此单账号测试导入。
每个检查点更新入口/状态/TODO/开发日志及机器todoItem。若任一阶段受阻,标明卡点和已发生的真实请求,不自动继续写入或修改原方案/证据掩盖差异。
## 7. 最终验收及恢复边界
| 项 | 成功条件 |
|---|---|
| 授权与基线 | 固定源/101映射已确认;方案和实施复核已裁决;执行时停写窗口明确 |
| 修改范围 | 仅Program两分支+两个新辅助C#;Client/配置/Import102与102 claim无变化 |
| 写前门 | 来源55项、配置、现有账号、空目标、候选Valid及持久化before/claim全部成功 |
| 至多一次 | 独立持久claim,最多一次Set;错误/Unknown/写盘失败均不重发 |
| 实际结果 | before空,after恰55原值且无额外键,Set明确成功,全部证据完整 |
| 边界 | 101独占窗口、无Play/permit/102/PF/Delete;新导入claim之外旧claims不变 |
| 交付口径 | 准确阶段请求计数、at本地钟限制、剩余迁移条件、无需回滚时不执行回滚 |
失败不是“恢复到空档”的授权:即使原before为空,也不得自动Delete所有键,因为Unknown/外部进度会使清空成为数据丢失。人工恢复另案需当时新鲜观察、差异和无新增进度证据;当前只保留源、claim及结果停止。本方案不承诺多键服务器事务或CAS,不为本专用测试引入新服务器能力。
## 8. 作者自审
已按真实Import102顺序检查并区别处理“set-result写盘失败会跳过观察”的缺口;明确claim在Set之前持久化、失败可能已写、崩溃不能保证观察、源码固定与测试接缝分离、真实导入前实施复核/停写两门。源的历史性、未知键保留、两种null原字符串、时间偏差及身份映射局限均已声明。未扩大游戏模块或通用迁移范围。
当前停在D0方案独立评审;用户来源决定已完成,不能再标为“待用户确认来源”。真实停写确认在D3发生,当前不打断用户进行无必要的提前停机。