Files
ft/CodexOutput/upserver/阶段3C独立方案审查-2026-09-23.md

7.8 KiB
Raw Blame History

阶段3C独立方案审查

日期:2026-09-23。审查者:独立方案审查AI(ZCode会话,由用户指定)。审查对象与范围:仅文档审查——3C方案、实施任务书、验收矩阵、方案审查清单;对照阶段3主方案、3B文档终审、服务端4xx核查及现有生产源码核对可执行性。本轮未修改任何生产代码、测试、配置或共享文档,仅新建本文件。

四份待审文档SHA256已逐项核对,与审查提示词所列基线完全一致(16718D63…/D0029D31…/8B00E33D…/3771CF20…)。

裁决

通过。 未发现P1/P2级方案问题。方案在现有生产接口内可完整实现,正式服安全边界(manifest先行、受限转发Handler、碰撞不删、Unknown停止、双会话清理确认)自洽且与已验收的3A/3B保守语义一致。下列6项均为P3级澄清建议,建议主会话裁决时采纳为实施说明,不要求重新评审轮次。

可行性核对(对照生产源码与服务端)

核对点 结论
受限Handler注入路径 PlayerKVSessionOwner.CreateAsync的internal重载接收handlerFactory/transportTimeout;测试宿主直接编译生产源码(同程序集)即可访问,3B的KVSyncCoordinatorTests已验证同一模式。可行
Owner初始化"全量Get"识别 PlayerKVClient.GetAsync(null)请求体不含keys;受限Handler可据此识别并改写为manifest keys。可行
服务端过滤Get语义 N3-Server-CN(858fe7c)PlayerKVService.GetAsync对带keys请求只返回存在的键。过滤Get不会返回越界key。可行
live模式登录与Owner传输分离 S101AccountSession自身handlerFactory与Owner的handlerFactory相互独立:登录走真实默认handler,Owner走"真实HttpClientHandler外包受限转发层"(Cookie由内层UseCookies=true处理)。可行
L0断言素材 S101AccountSession.PlayerId/NewlyCreated公开暴露。可行
O5诊断素材 PlayerKVSyncState.Open/Inspect()公开;KVIdentity环境指纹规则=规范API的SHA256,与诊断"API指纹"字段同源。可行
key长度 前缀codex_kv_s3c_YYYYMMDD_<32位guid>约53字符+后缀,满足服务端128字符上限。可行
§2.3不扩展KVLiveTests的理由 核实KVLiveTests.csproj确用PlayerData/*.cs通配符;扩展会破坏阶段边界(同3B步骤0.1裁决的问题)。理由成立
回归与hash门 42/86/29命令、3B终审基线hash门与既有证据链一致。可行

发现清单(全部P3,不阻塞)

F1|§4.2.4措辞:"发现空集合以外的越界key时…拒绝"含糊。 触发:Get无keys是Owner初始化的正常形态(必须改写转发而非拒绝);Set空data是服务端no-op。影响:实施者可能把Owner初始化Get当越界拒绝。最小建议:改写为"Get无keys改写为manifest keys;Set/Delete请求中出现任何不属于manifest的key即在网络前整体拒绝(Set空data按no-op放行)"。

F2|越界拒绝经完整Owner链路时的表象是Unknown冻结。 触发:PlayerKVClient对变更请求的一切非200/传输异常均保守标MayHaveCommitted=true,3B据此冻结Unknown——这是正确保守行为,但H2若经Owner全链路验证,预期结果是Unknown而非"确定失败"。影响:实施者可能误判为缺陷或试图让拒绝"确定失败"。最小建议:H2按任务书检查点4保持单元级断言(对受限Handler直接断言内部转发计数=0);如经Owner链路,应使用一次性run目录并断言"计数=0且状态冻结为Unknown"。建议在§4.2补一句说明。

F3|§7"票据状态计数"来源未指明。 Inspect()不含票据信息,Owner关闭后coordinator不可查询。影响:实施者可能寻找不存在的生产接口。最小建议:明确由测试宿主在关闭Owner前通过票据对象、WaitForTicketAsync结果和FlushAsync汇总自行累计。

F4|O2子进程A依赖"干净Close后退出"。 Close不隐式Flush的契约以A正常收尾(释放文件锁、无遗留tmp)为前提;父进程超时强杀只能作为失败路径。影响:若把强杀当正常步骤,B会读到RecoveryRequired而非pending。最小建议:O2注明"A正常Close并退出后才启动B;超时强杀视为该用例失败并保留run目录"。

F5|§2.1未写非法参数退出码,矩阵S0要求非0。 最小建议:方案补"以非0退出码退出"。

F6|manifest允许保存完整测试key,日志合同可再收紧。 manifest在任务目录内可接受;但建议§8明确result.json与log同样不得回显完整key(只出现runId/数量/状态),与D3诊断规则保持一致。

审查清单逐项

A. 范围与前置门 — PASS

  • PASS:3C只做组合、限定fixture、诊断与证据,不接游戏入口(方案§1)。
  • PASS:默认无生产文件修改;接口不足返回差异评审(方案§3;且本审查确认现有接口足够,见可行性核对)。
  • PASS:主会话实施、其他AI本轮只审(任务书§1前置门)。
  • PASS:API/账号/模式/缓存根固定(§1、§4.3,路径限定于runs//cache)。

B. 正式服安全 — PASS

  • PASS:manifest在任何变更前落盘,key预登记,运行时不可扩大(§4.1+Handler白名单固定于manifest)。
  • PASS:碰撞预检失败不删碰撞key且cleanupAllowed=false(L0)。
  • PASS:受限Handler以"Get改写为manifest keys+响应仅含manifest key"阻断业务KV进入测试缓存(§4.2.3;服务端语义已核实)。
  • PASS:Set/Delete越界网络前拒绝;禁重定向/重试/全量清理(§4.2)。
  • PASS:正式服不制造Unknown/超时/断网/4xx/迟到写(§1;矩阵注明故障仅合成)。
  • PASS:Unknown→停止写、hadUnknownWrite落盘、原运行永久失败;--cleanup不改写通过、不解除生产Unknown(L4)。
  • PASS:清理只按manifest、双新会话读回为空(L3/L6)。

C. 组合和并发 — PASS

  • PASS:离线直接使用真实Owner+3A状态+3B协调器,不重实现状态机(§5)。
  • PASS:跨进程pending/文件锁步骤有确定超时(O2;F4为补充说明)。
  • PASS:诊断导出只读(关闭后经Inspect()),不改变Unknown/RecoveryRequired策略(O5)。
  • PASS:101/102命名空间隔离与API指纹隔离均有明确用例(O3/O4;"IP与s101.local不视为同环境"与3A KVIdentity一致)。

D. 诊断隐私 — PASS

  • PASS:专用DTO,不直接序列化DiagnosticSnapshot(§7)。
  • PASS:禁止Cookie/Token/正文/值/业务key/原始测试key/用户名路径;key仅SHA256(缓存根以任务内相对路径记录,规避了用户名路径)。
  • PASS:数量、序号、状态、错误类别足以定位问题。
  • PASS:诊断失败不改缓存、不删证据(O5)。

E. 可执行性和验收 — PASS

  • PASS:允许文件、命令、结果格式、退出条件完整无歧义(任务书§2/§4/§5;F5为补充)。
  • PASS:无TBD/TODO;不依赖服务端CAS/revision/幂等查询;阶段2证据不复用为3C证据(验收门要求全新执行)。
  • PASS:42/86/29回归、Unity编译、生产hash一致、实施后独立审查均为强制门(§9)。
  • PASS:清理失败、Unknown、越界请求、生产hash变化均明确阻塞(§9末段)。
  • PASS:真机、游戏接线、PlayFab替换/SDK删除明确未完成(§1、§9)。

审查者边界声明

本轮仅新建本审查文件;未修改任何生产代码、测试、csproj、方案文件或共享README/TODO/开发状态/接手入口。裁决权归主会话:建议将F1–F6采纳为实施说明并把任务书状态改为"已批准实施"后开始3C开发。