docs:勾选 Plan §5 / §8 Phase 1.10-1.13 进度 + CLAUDE.md 新增「进度同步规范」

把 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 进度已勾选
This commit is contained in:
joywayer
2026-06-22 02:30:31 +08:00
parent 0887088bb8
commit 87304552c3
2 changed files with 26 additions and 8 deletions
+18
View File
@@ -177,3 +177,21 @@ H5 页面通过特定的 JavaScript 协议初始化桥并注册自己的 handler
- 改动已通过最小验证(BuildProject 通过 / 关键路径手测通过 / 单测通过)→ 立即提议 - 改动已通过最小验证(BuildProject 通过 / 关键路径手测通过 / 单测通过)→ 立即提议
- 工作树还有其它无关待提交内容 → 先帮用户分类、按职责拆 commit,**不混提** - 工作树还有其它无关待提交内容 → 先帮用户分类、按职责拆 commit,**不混提**
- 用户未明确同意前不直接执行 `git commit`,但应当**清晰地告诉用户"现在适合提交了"**,并附上建议的 commit message - 用户未明确同意前不直接执行 `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 是全局视角
+8 -8
View File
@@ -262,23 +262,23 @@ Contract Design Plan(本文档)
##### 1.D 远程配置 + 版本对比 + zip 升级(**Phase 1 关键缺口,ADR-008** ##### 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` 就是这样构造) - URL 构造:`http://` + `BundleConfig.shared.gameConfig.replacingOccurrences("-", "/")` + `.txt`**注意**:不带 `SERVERNew` 前缀,原 `daoqi/NewRootVC.m:250` 就是这样构造)
- 拉远端 `.txt` 内容(实际是 JSON)→ `URLSession.async data(from:)`10 s 超时 - 拉远端 `.txt` 内容(实际是 JSON)→ `URLSession.async data(from:)`10 s 超时
- 指数退避重试(msext 用 4s 等间隔暴力 timer,新外壳用 1s / 2s / 4s 三次) - 指数退避重试(msext 用 4s 等间隔暴力 timer,新外壳用 1s / 2s / 4s 三次)
- 反 JSON → 强类型 `RemoteConfig` Codable 模型(嵌套:`agentlist[].channellist[].marketlist[]` + `agentlist[].gamelist[].channellist[].marketlist[]` - 反 JSON → 强类型 `RemoteConfig` Codable 模型(嵌套:`agentlist[].channellist[].marketlist[]` + `agentlist[].gamelist[].channellist[].marketlist[]`
- 单测:mock URLSession + fixture JSON 验证嵌套解析正确 - 单测: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 - 输入:`RemoteConfig + (agentId, channelId, marketId, gameId)` 四组 ID
- 输出:`ResolvedVersion { appVersion, appDownload, gameVersion, gameZip }` - 输出:`ResolvedVersion { appVersion, appDownload, gameVersion, gameZip }`
- 算法:4 级覆盖合并 —— 顶层 → 当前 agent → 当前 channel → 当前 market(最深胜出);agent 子树和 game 子树分别合并,game 子树最终覆盖 agent 子树 - 算法:4 级覆盖合并 —— 顶层 → 当前 agent → 当前 channel → 当前 market(最深胜出);agent 子树和 game 子树分别合并,game 子树最终覆盖 agent 子树
- 参考 `daoqi/NewRootVC.m:1372-1492` 的散落 if/else,新外壳实现成纯 reduce - 参考 `daoqi/NewRootVC.m:1372-1492` 的散落 if/else,新外壳实现成纯 reduce
- 单测:覆盖所有边界(顶层有/无、深层缺失、agent vs game 优先级 4 组场景至少 12 用例) - 单测:覆盖所有边界(顶层有/无、深层缺失、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 - `localAppVersion: Int`:从 `BundleConfig.shared.appVersion` 读,转 Int
- `localGameVersion: Int`:读 `Library/Caches/{gamedir}/{gamestart}/version.xml``XMLParser` 解析 `/game/version@value` 转 Int;缺失返回 0 - `localGameVersion: Int`:读 `Library/Caches/{gamedir}/{gamestart}/version.xml``XMLParser` 解析 `/game/version@value` 转 Int;缺失返回 0
- 单测:fixture xml 验证解析;缺失 / 损坏 xml 返回 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` - `upgradeIfNeeded(remote: ResolvedVersion) async throws -> UpgradeOutcome`
- 比较 `remote.gameVersion > LocalVersionReader.localGameVersion` → 触发下载,否则直接返回 `.noop` - 比较 `remote.gameVersion > LocalVersionReader.localGameVersion` → 触发下载,否则直接返回 `.noop`
- 下载链路:`URLSession.download(from:)` → 临时文件 `tmp/lobbyzip-{uuid}.zip` → ZIPFoundation 解压到 `tmp/lobbyzip-{uuid}/`**原子 rename**`lobbyRoot`(解压前清空旧 lobbyRoot;不学 msext "目录名 +1" hack - 下载链路:`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.7 BridgeBus
- [x] 1.8 WVJB JS 协议 - [x] 1.8 WVJB JS 协议
- [x] 1.9 BridgedWebView - [x] 1.9 BridgedWebView
- [ ] 1.10 RemoteConfigClientactorURLSession async + 重试) - [x] 1.10 RemoteConfigClientactorURLSession async + 重试 + cache-busting + 短文本 + FlexibleString
- [ ] 1.11 VersionResolver(纯函数 4 级覆盖 + 单测 - [x] 1.11 VersionResolverchulishengji 双子树合并算法,纯函数)
- [ ] 1.12 LocalVersionReaderversion.xml 解析) - [x] 1.12 LocalVersionReaderversion.xml 解析 + ATS 全局放行
- [ ] 1.13 LobbyZipUpgraderactor,原子 rename 升级) - [x] 1.13 LobbyZipUpgraderactor,原子 rename 升级;端到端实测 260→261
- [ ] 1.14 WebContainerViewController16:9 letterbox + 串接整链路) - [ ] 1.14 WebContainerViewController16:9 letterbox + 串接整链路)
- [ ] 1.15 VibratorHandler - [ ] 1.15 VibratorHandler
- [ ] 1.16 SceneDelegate → WebContainerViewController - [ ] 1.16 SceneDelegate → WebContainerViewController