From 87304552c370b7e4653924adec024837326582fa Mon Sep 17 00:00:00 2001 From: joywayer Date: Mon, 22 Jun 2026 02:30:31 +0800 Subject: [PATCH] =?UTF-8?q?docs=EF=BC=9A=E5=8B=BE=E9=80=89=20Plan=20=C2=A7?= =?UTF-8?q?5=20/=20=C2=A78=20Phase=201.10-1.13=20=E8=BF=9B=E5=BA=A6=20+=20?= =?UTF-8?q?CLAUDE.md=20=E6=96=B0=E5=A2=9E=E3=80=8C=E8=BF=9B=E5=BA=A6?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E8=A7=84=E8=8C=83=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 把 Plan 任务清单和进度追踪 checklist 同步到当前实际进度(1.10-1.13 均已完成 + 实测验证),并在 CLAUDE.md 写入"每完成 Phase 子项必须立即 更新 Plan 进度"的硬约束。 - Plan §5 Phase 1.10-1.13 任务清单 [ ] → [x] - Plan §8 进度追踪同步:标注算法名 / 端到端实测结果(260→261 验证) - CLAUDE.md 新增「进度同步规范」节,规定: - Plan §5 + §8 每完成子项立即勾选,与 commit 同 commit 内完成 - 任务描述与实现有差异时同步修订(不允许 Plan 与代码长期不一致) - commit message 末尾备注"Plan 进度已勾选" - 理由:Plan 是项目记忆的唯一权威进度视图,新人 clone 后看 Plan §8 即可知整体进度;与 commit log 双写互为校验 Plan 进度已勾选 --- CLAUDE.md | 18 ++++++++++++++++++ docs/Development-Plan.md | 16 ++++++++-------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3e5dee3..634fac0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -177,3 +177,21 @@ H5 页面通过特定的 JavaScript 协议初始化桥并注册自己的 handler - 改动已通过最小验证(BuildProject 通过 / 关键路径手测通过 / 单测通过)→ 立即提议 - 工作树还有其它无关待提交内容 → 先帮用户分类、按职责拆 commit,**不混提** - 用户未明确同意前不直接执行 `git commit`,但应当**清晰地告诉用户"现在适合提交了"**,并附上建议的 commit message + +--- + +## 进度同步规范 + +**每完成一个 Phase 子项(如 1.10 / 1.11 / 1.12 ...)必须立即更新 `docs/Development-Plan.md` 的进度标记**,不允许"代码提交但 Plan 未同步"的滞后。具体: + +- Plan §5 各 Phase 任务清单:完成的子项把 `- [ ]` 改 `- [x]`,附简短结果或耗时 +- Plan §8 进度追踪 checklist:同步勾选 + 简短补充(如算法名 / 端到端测试结果) +- 时机:通常与该子项的 `git commit` 同一 commit 内完成,commit message 末尾备注"Plan 进度已勾选" +- 若改动跨多个 Phase 子项(极少见),逐项勾选不能漏 +- 当任务清单中的描述与最终实现有差异(如新增子任务、合并子任务、改变实现策略)时,**同步修改任务描述**,不允许 Plan 文档与代码长期不一致 + +理由: +- Plan 是项目记忆的唯一权威进度视图,新人 clone 后只看 Plan §8 就能立刻知道整体进度 +- 若代码已完成但 Plan 未勾选,新人会误判项目状态、做重复工作 +- 若 Plan 描述与代码实现不一致,未来 Phase 验收 / 回归测试时会基于错误前提 +- 与 commit message 双写互为校验:commit log 是细节,Plan 是全局视角 diff --git a/docs/Development-Plan.md b/docs/Development-Plan.md index 17b0e43..a596845 100644 --- a/docs/Development-Plan.md +++ b/docs/Development-Plan.md @@ -262,23 +262,23 @@ Contract Design Plan(本文档) ##### 1.D 远程配置 + 版本对比 + zip 升级(**Phase 1 关键缺口,ADR-008**) -- [ ] **1.10** 实现 `ylgamehall/Source/Network/RemoteConfigClient.swift`(actor) +- [x] **1.10** 实现 `ylgamehall/Source/Network/RemoteConfigClient.swift`(actor) - URL 构造:`http://` + `BundleConfig.shared.gameConfig.replacingOccurrences("-", "/")` + `.txt`(**注意**:不带 `SERVERNew` 前缀,原 `daoqi/NewRootVC.m:250` 就是这样构造) - 拉远端 `.txt` 内容(实际是 JSON)→ `URLSession.async data(from:)`,10 s 超时 - 指数退避重试(msext 用 4s 等间隔暴力 timer,新外壳用 1s / 2s / 4s 三次) - 反 JSON → 强类型 `RemoteConfig` Codable 模型(嵌套:`agentlist[].channellist[].marketlist[]` + `agentlist[].gamelist[].channellist[].marketlist[]`) - 单测:mock URLSession + fixture JSON 验证嵌套解析正确 -- [ ] **1.11** 实现 `ylgamehall/Source/Network/VersionResolver.swift`(纯函数 + 单测) +- [x] **1.11** 实现 `ylgamehall/Source/Network/VersionResolver.swift`(纯函数 + 单测) - 输入:`RemoteConfig + (agentId, channelId, marketId, gameId)` 四组 ID - 输出:`ResolvedVersion { appVersion, appDownload, gameVersion, gameZip }` - 算法:4 级覆盖合并 —— 顶层 → 当前 agent → 当前 channel → 当前 market(最深胜出);agent 子树和 game 子树分别合并,game 子树最终覆盖 agent 子树 - 参考 `daoqi/NewRootVC.m:1372-1492` 的散落 if/else,新外壳实现成纯 reduce - 单测:覆盖所有边界(顶层有/无、深层缺失、agent vs game 优先级 4 组场景至少 12 用例) -- [ ] **1.12** 实现 `ylgamehall/Source/Resource/LocalVersionReader.swift`(nonisolated namespace) +- [x] **1.12** 实现 `ylgamehall/Source/Resource/LocalVersionReader.swift`(nonisolated namespace) - `localAppVersion: Int`:从 `BundleConfig.shared.appVersion` 读,转 Int - `localGameVersion: Int`:读 `Library/Caches/{gamedir}/{gamestart}/version.xml` 用 `XMLParser` 解析 `/game/version@value` 转 Int;缺失返回 0 - 单测:fixture xml 验证解析;缺失 / 损坏 xml 返回 0 -- [ ] **1.13** 实现 `ylgamehall/Source/Resource/LobbyZipUpgrader.swift`(actor) +- [x] **1.13** 实现 `ylgamehall/Source/Resource/LobbyZipUpgrader.swift`(actor) - `upgradeIfNeeded(remote: ResolvedVersion) async throws -> UpgradeOutcome` - 比较 `remote.gameVersion > LocalVersionReader.localGameVersion` → 触发下载,否则直接返回 `.noop` - 下载链路:`URLSession.download(from:)` → 临时文件 `tmp/lobbyzip-{uuid}.zip` → ZIPFoundation 解压到 `tmp/lobbyzip-{uuid}/` → **原子 rename** 到 `lobbyRoot`(解压前清空旧 lobbyRoot;不学 msext "目录名 +1" hack) @@ -781,10 +781,10 @@ H5 调 `OpenurlTitleData` 打开弹层 WebView,弹层内 H5 用 `window.settin - [x] 1.7 BridgeBus - [x] 1.8 WVJB JS 协议 - [x] 1.9 BridgedWebView -- [ ] 1.10 RemoteConfigClient(actor,URLSession async + 重试) -- [ ] 1.11 VersionResolver(纯函数 4 级覆盖 + 单测) -- [ ] 1.12 LocalVersionReader(version.xml 解析) -- [ ] 1.13 LobbyZipUpgrader(actor,原子 rename 升级) +- [x] 1.10 RemoteConfigClient(actor,URLSession async + 重试 + cache-busting + 短文本 + FlexibleString) +- [x] 1.11 VersionResolver(chulishengji 双子树合并算法,纯函数) +- [x] 1.12 LocalVersionReader(version.xml 解析 + ATS 全局放行) +- [x] 1.13 LobbyZipUpgrader(actor,原子 rename 升级;端到端实测 260→261) - [ ] 1.14 WebContainerViewController(16:9 letterbox + 串接整链路) - [ ] 1.15 VibratorHandler - [ ] 1.16 SceneDelegate → WebContainerViewController