Files
ft/CodexOutput/upserver/阶段4B分期与4B1方案独立审查-2026-09-23.md

60 lines
6.7 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.
# 阶段4B分期与4B1方案独立审查
日期:2026-09-23。审查者:用户指定外部AI(ZCode会话)。性质:**方案设计审查,非实施**。本轮只读:未修改生产代码/测试/csproj/共享文档/原报告,未使用子Agent,未访问正式服,未运行Unity(设计审查无需)。
## 受审版本核对
`阶段4B1方案受审基线-2026-09-23.json`共18项(方案、提示词、4A裁决/复核、阶段4分期、8份生产源码、UserService/PlayFabMgr/AggUserService/DataCenter.PlayerData/QuitAct、4A测试Program/csproj),本审查逐项重算SHA256——**全部一致,无受审版本漂移**。
实际读取范围:上述文档全文;源码抽查§1观察表(UserService InitData/LoginApiServer/401订阅/Logout Dispose apiSession、AggUserService OnInitData订阅窗口、QuitAct StartAsync次序、PlayFabMgr既有4A期核对);4A Bridge/Handle/Owner/Result实现(本轮及前轮已深读)。
## 双裁决
- **4B分期:PASS。** 4B1准备内核/4B2游戏加载/4B3普通读写路由的三分各有明确"编码前所需审查"与"启用范围";4B1白名单之外无任何放行;4C消费、身份专项、tickets有界历史、正式场景数据边界仍被门控;"4B1通过≠4B通过≠G4通过"的层级明确。
- **4B1方案:PASS(附3条P3建议,均不阻塞)。** 职责边界(拥有session、认证→Open→Read→快照、Close后才Dispose session)正确复用4A而非复制调度器;生命周期合同完整可实现;矩阵充分。
## 逐项审查
### 1. 分期合理性与白名单(§2/§6):通过
一次性4B的否决理由成立(缺完整逐调用点清单,§2表);按key混用维持原否决。§6白名单仅两份新生产文件+meta+独立测试目录+文档;明令禁改S101AccountSession/4A/3A/3B/KV Client/旧测试/UserService/PlayFabMgr/DataCenter/Stages/服务器/包;"发现接口不足先记差异再补审"与既有流程一致。**无4B2/4B3偷渡路径。**
### 2. 4B1新增价值与4A复用(§3):通过
与4A分工清晰:4A"外部已认证、不拥有session";4B1"建立session、按序取档、Close后才销毁"。复用4A的串行/票据/读接口(`Bridge.OpenAsync`/`Handle.ReadLocalSnapshot`/`Bridge.CloseAsync`均为4A公开面),不复制调度器。session所有权单一(context建、context在Bridge Close后Dispose),符合4A"桥接不拥有session"的反向合同。
### 3. 生命周期与并发(§4):通过,附P3
- 单次Prepare:同实例返回同一任务、首token定义生命期(B1-06)✓。
- **workSettled与对外结果分离**(§4.3):Close不等待对外Prepare任务、失败路径先完成workSettled再走统一Close——互等环被显式排除;即使workSettled完成仍await Bridge.CloseAsync ✓。
- Close-before-Prepare:永久关闭+后续Prepare零请求Cancelled(B1-11)✓。
- 资源次序(§4.4):撤销准入→取消CTS→await workSettled→await Bridge.Close→Dispose session→释放CTS——与4A Slot的Cancel/Dispose串行化兼容;Bridge Close失败保留session/bridge/故障任务并永久阻塞实例,不允许new绕过(§4.2)✓。
- 取消回调只同步撤销+非阻塞收尾,后台收尾任务可被Close观察(§4.3)✓。
- 外部委托约束(§4.5)逐条落实4A裁决P3-3(只读、无I/O、不等待、异常即失效、同号重登以epoch区分)✓。
### 4. Prepared语义与空快照(§3.1/§5):通过
Prepared≠可玩/有效业务档/建档批准;成功空快照显式`IsEmpty=true`且"来源于成功Get";失败Data=null;§5把空档阻塞、业务校验、初始化副作用、回调隔离、PF暂留边界全部明确推给4B2/4B3并给出默认策略方向——不存在被误解为初始化放行的措辞漏洞。
### 5. 可实现性(§6/现有API):通过
- session注入:`S101AccountSession(Uri, Func<CookieContainer,HttpMessageHandler>)`构造已存在(3B/3C测试同模式)。
- bridge注入:`PlayerKVGameSessionBridge`的internal handlerFactory构造已存在(4A测试同模式)。
- 其余全部走4A公开面。**无需扩大任何既有文件边界或公共接口**;PlayerKVLoginContext自带internal工厂钩子供测试故障注入,符合"不新增公共任意传输入口"。
### 6. 矩阵B1-01~B1-16(§7):充分
覆盖正常链、输入校验/0传输、认证失败清理、Open/Read各失败态、空快照newCreate两态、重复Prepare、忽略取消(认证/Get)下Close、换号排水、epoch失效、Prepare/Close并发、幂等/重入、清理异常保留、委托/工厂异常、既有缓存pending/Unknown只读呈现、脱敏、回归与编译。TCS闸门+整体超时+采纳P3-1(保留完整stdout/stderr与真实退出码、失败证据不被后续成功覆盖)✓。真实游戏接线、消费、身份、tickets、正式数据边界均仍受门控(§5/§2)✓。
## 发现清单(全部P3,不阻塞)
**P3-A|建议矩阵补一条显式断言:Close后`IsCurrent=false`且已返回快照"仍可读但不再授权"。** §3.1把IsCurrent定为业务侧合同,但B1-02/09/11只隐含覆盖。建议并入B1-11或B1-09:断言Close后IsCurrent=false、旧快照对象仍可枚举(不被销毁)、再Prepare被拒。位置:§7矩阵。
**P3-B|"失败结果在清理完成前不完成"意味着Prepare可能无界等待——建议接口文档显式声明。** §3.2 Cancelled行规则正确(与4A/3C忽略取消的保守收尾一致),但调用方易假设PrepareAsync必有界返回。建议在PlayerKVLoginPrepareResult/PrepareAsync的文档注释中写明:"取消/失败结果在底层传输与Close真正收尾前不完成;忽略取消的传输可使本任务长期挂起,收尾状态以CloseAsync为准"。位置:§3.1/§3.2。
**P3-C|重复Prepare"首token定义生命期"是易误用点。** B1-06已定义并有测试,建议生产代码以XML文档+Debug.Assert级防护(重复调用token不同不静默接受)双保险,避免4B2接线时误传新token以为能延长生命期。位置:§3.1。
## 源码观察核对(方案§1)
抽查确认:UserService 501–503行InitData在登录链内调用、151–155行401事件触发后台`_ = LoginApiServer()`、Logout多处`apiSession?.Dispose()`;AggUserService 93/97行OnInitData订阅仅限base.Login等待期(try/finally退订);QuitAct.StartAsync先撤销销毁再await;PlayFabMgr 40秒任务/void写/全局Instance与4A期核对一致。方案§1观察表**无失实**。
## 结论
- **4B分期:PASS。**
- **4B1方案:PASS(P3-A/B/C三条建议,建议随裁决采纳为实施说明)。**
- 本报告仅为方案审查:不构成实施批准、不等于4B任何单元验收;P3-1(KVSyncTests偶发失败)按4A关闭裁决口径保留为待查项,本方案§7的采纳表述准确。