diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 5849dda..222a00e 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -181,12 +181,13 @@ Plans: 3. 移动端(窄屏)布局下,同一场景正常显示且可操作 4. 数据端与渲染端保持双端分离——数据端无 DOM,仍可在 Node 环境跑回放验证 -**Plans**: 12 已执行 + 1 已规划待执行(04-13) — 04-01..04-07 已执行(04-06 = material 接口影响范围清点·只读;04-07 = material 接口适应实施·第二步);04-08(TextureManager 新接口影响范围清点·只读,D-38/D-39/D-40/D-41/D-42 第一步)已执行;04-09(TextureManager 新接口适应实施·第二步·代码改动,D-43..D-50)已执行;04-10(渲染端结构性重构影响范围清点·只读,D-51..D-57 第一步)已执行;04-11(渲染端结构性重构·第二步·移植实施·代码改动,D-58..D-68)已执行;04-12(04-11 落地修正·桶导出边界 + 消费者直连 + shared 单文件·代码改动,D-69/D-70)已执行(异地执行,commit 81e4e98;`8b114c7 docs(04-12): sync tracking`);**04-13(部分 UI 的进一步接口适配·第一步·只读收集,D-71/D-72/D-73)已规划待执行**(执行前须 Task 0 汇报关卡等待用户「可以执行」);渲染适配与移动端/桌面端双布局(REND-01/REND-02)整体仍未完成 +**Plans**: 13 已执行 + 1 已规划待执行(04-14) — 04-01..04-07 已执行(04-06 = material 接口影响范围清点·只读;04-07 = material 接口适应实施·第二步);04-08(TextureManager 新接口影响范围清点·只读,D-38/D-39/D-40/D-41/D-42 第一步)已执行;04-09(TextureManager 新接口适应实施·第二步·代码改动,D-43..D-50)已执行;04-10(渲染端结构性重构影响范围清点·只读,D-51..D-57 第一步)已执行;04-11(渲染端结构性重构·第二步·移植实施·代码改动,D-58..D-68)已执行;04-12(04-11 落地修正·桶导出边界 + 消费者直连 + shared 单文件·代码改动,D-69/D-70)已执行(异地执行,commit 81e4e98;`8b114c7 docs(04-12): sync tracking`);**04-13(部分 UI 的进一步接口适配·第一步·只读收集,D-71/D-72/D-73)已执行**(交付 `04-UI-ADAPTATION-IMPACT.md`;A7/B5/C2/D5/E4/F5 + 8 行修改点汇总);**04-14(部分 UI 的进一步接口适配·第二步·适配实施,D-74..D-78)已规划待执行**(执行前须 Task 0 汇报关卡等待用户「可以执行」);渲染适配与移动端/桌面端双布局(REND-01/REND-02)整体仍未完成 **Plan register note (2026-09-21):** 第五个增量 `04-05`(收口 `#04-01-M-06` / `#04-01-M-09`:状态栏 9 个数值属性改经 `client.hero.attribute.getFinalAttribute(...)` 读取 + 逐图层钩子类 `RendererLayerHook`,2026-09-20 执行)此前未登记进本路线图,本次一并补登;第六个增量 `04-06` = material 接口适应(D-30/D-31/D-32)**第一步·只读影响清点**,产出 `04-MATERIAL-INTERFACE-IMPACT.md`,第二步(实施)待用户审阅后另行规划。第七个增量 `04-07` = material 接口适应实施(**第二步·代码改动**,D-33..D-37),2026-09-22 已执行(10 文件原子提交)。**第八个增量 `04-08` = TextureManager 新接口适应(D-38..D-42)第一步·只读影响清点**,产出 `04-TEXTURE-INTERFACE-IMPACT.md`;第二步(实施)待用户审阅后另行规划。 **Plan register note (2026-09-24):** 第十个增量 `04-10` = 渲染端结构性重构(用户裁定 2026-09-23 · D-51..D-57)**第一步·只读影响清点**:把 `packages-user/client-modules/src/render/` 下的通用内容(`render/components`、`render/elements`、`render/map` 三文件夹 + `render/utils/layout.ts`,规划日实测 **39** 文件)迁往渲染系统层 `packages-user/client-base/src/`(D-51/D-52;目标布局按 D-53:`client-base/src/{components,elements,map}/` + `client-base/src/layout/`,**不新建 `render/` 目录**);不移植 `render/utils/` 其余(`index.ts`/`saves.ts`/`use.ts`)与 `ui`/`fx`/`weather`/`render` 顶层文件(D-54);legacy 一律只报告(D-55);用户并发改动零触碰(D-56)。台账按 A 被移集合内部交叉引用 / B 外部导入点(含 `entry-client` / `entry-data` / `packages/legacy-ui` 经 `@user/client-modules` 公共面的间接依赖)/ C barrel 与导出面 / D 分层与循环依赖风险(`client-base` 不得反向依赖 `client-modules`,含 `package.json` 依赖现状)/ E legacy 命中(仅报告)/ F 未确定 六类分组,每行精确到文件或符号 + `file:line` 且 ≥2 锚点;产出 `04-STRUCTURE-MIGRATION-IMPACT.md`,生产代码零改动且**零命名变更**;**已规划待执行**,执行前须 Task 0 汇报关卡等待用户「可以执行」;第二步(移植实施)待用户审阅后另行规划;`REND-01` / `REND-02` 仍 Pending。 **Plan register note (2026-09-24 · 04-11 第二步):** 第十一个增量 `04-11` = 渲染端结构性重构**第二步·移植实施(代码改动)**(用户裁定 2026-09-24 · D-58..D-68)。**移植按 MOVE 落地**:把 D-52 的 **39** 文件(`render/components` 13 / `render/elements` 5 / `render/map` 20 / `render/utils/layout.ts` 1)按 D-53 放进 `packages-user/client-base/src/{components,elements,map}/` 与 `client-base/src/layout/layout.ts`(并新增 `layout/index.ts`,D-61),**并删除 `client-modules/src/render/` 下的原件**;D-58 另新建 `client-base/src/shared/`(从 `client-modules/src/shared.ts` 摘取被移 `map` 文件所需的 10 个常量,原文件保留);D-63 `client-base/src/index.ts` 新增 `./components` / `./elements` / `./map` / `./layout` / `./shared` 五条桶导出,实现层消费者(`render/ui` 8 文件 11 处含 `toolbar.tsx` 两处深层导入、`fallback/ui.ts:2`、`render/index.tsx:5,45,46`、`client.ts:25`、`types.ts:2`)改指 `@user/client-base`;被移 `map` 文件的 shared 说明符改指 4 处(D-58);D-59 / D-68 的架构耦合(`render/renderer` 3 处 + `render/use.ts` 5 处)与 D-60 的 `render/utils/` 断链(`index.ts:1` / `saves.ts:2`)共 **10** 处以带原因的 `// @ts-expect-error` 暂记,**不反向引用、不解耦**;D-66 `client-base/package.json` 新增 5 条 `workspace:*` 依赖(`@motajs/animate` / `@motajs/common` / `@motajs/render-vue` / `@motajs/system` / `@user/data-common`)并执行 `pnpm i`;D-64 `?raw` 不处理、D-65 legacy 一律不管、D-62 命名一律不变;门禁 = 目标落位 / 移动已发生 / 桶导出 / 依赖与 lockfile / 消费者改指 / 反向引用零命中 / `@ts-expect-error` 落点(10 处新增 + 既有 7 条保留)/ 39 文件允许差异 / 实现层差异 / 范围与用户改动 / 无 `import type` / CRLF 共 12 道 + 计划文件范围门禁 + 人工复核(**D-68 明示不要求可运行、不要求零类型错误,故不设 `check:type` / `build` / TS 诊断数类门禁**);`autonomous: false`(Task 0 汇报关卡);第二步的后续余留(自包引用未验证 / 两行同源星导出 / 8 处反向耦合接线 / D-60 断链)由用户接手;`REND-01` / `REND-02` 仍 Pending。 **Plan register note (2026-09-24 · 04-12 落地修正):** 第十二个增量 `04-12` = **04-11 落地修正·桶导出边界 + 消费者直连 + `shared` 单文件(代码改动)**(用户裁定 2026-09-24 · **D-69 / D-70**)。**D-69(桶导出边界原则,硬约束)**:一个 barrel(`index.ts` / `index.tsx`)**只允许 `export * from './<同目录下的文件或子文件夹>'`**、不得导出本文件夹之外的任何内容;反过来,任何需要被移内容的**消费端必须直接 `import ... from '@user/client-base'`**、**不得经任何 barrel 转发导出**。**D-70 的 6 条修正**:① `client-modules/src/render/utils/index.ts` **删除**指向 `./layout` 的越界转发 `export *` 及其 `@ts-expect-error`(保留 `./saves` / `./use`);② `render/ui/save.tsx` **拆分** `../utils` 导入(`adjustGrid` / `IGridLayoutData` → `@user/client-base`,`getSave` / `SaveData` 仍留 `../utils`)、`render/ui/title.tsx` 的 `adjustCover` → `@user/client-base`;③ `render/utils/saves.ts` 的 `getConfirm` / `waitbox` → `@user/client-base` 并**删除**其 `@ts-expect-error`;④ `client-base/src/shared/{shared.ts,index.ts}` **折成单文件** `client-base/src/shared.ts` 并删除 `shared/` 目录(`client-base/src/index.ts` 的 `export * from './shared'` 与 `map/*` 的 `'../shared'` / `'../../shared'` 说明符**不受影响**,无需改动);⑤ **逐字保留** `client-base/src/{components/{choices,input,misc,scroll,textboxTyper,tip}.tsx,elements/index.ts}` 内 **8** 处引用 `render/use.ts` / `render/renderer` 的架构耦合 `@ts-expect-error`(属 D-59 / D-67,留待用户收尾;`textboxTyper.ts` 另有 1 处既有 `无法推导` 标注同属保留,故这 7 文件合计 9 处);⑥ **不得**新增任何转发导出,`client-modules/src/render/index.tsx` **维持现状**(用户已删除越界两行,现合规)。旁注(仅报告):`packages/client/src/index.ts:1` 的 `export * from '@motajs/client-base'` 亦违反 D-69,但在 `packages/`(D-33 只报告)。改动面 = `client-base/src/shared.ts` + `client-modules/src/render/{utils/index.ts,utils/saves.ts,ui/save.tsx,ui/title.tsx}` 五个生产文件 + `files_deleted` 的 `client-base/src/shared/{shared.ts,index.ts}`;门禁 = 桶边界(8 个 in-scope barrel 的 `export * from` 全为 `./` 前缀 + `render/utils/index.ts` 不再转发 `./layout`)/ F1..F4 收口自检 / F5 的 8 处标注逐字保留 + porcelain 零命中 / 范围与用户改动 / CRLF + 人工复核(**不设 `check:type` / `build` / TS 诊断数类门禁**,D-68 的延续);`autonomous: false`(Task 0 汇报关卡等待用户「可以执行」);无依赖变更、不执行 `pnpm i`;本步不引入任何新命名;`REND-01` / `REND-02` 仍 Pending。 **Plan register note (2026-09-30 · 04-13 第一步):** 第十三个增量 `04-13` = **部分 UI 的进一步接口适配·第一步·只读收集需要修改的点(只读)**(用户裁定 2026-09-25 · **D-71 / D-72 / D-73**)。**要解决的问题**:数据端经 `d8fa4b8 refactor: Delete global loading & hook object` 与加载系统重构后,渲染端部分 UI 仍以旧接口编写——① 全局 `loading` / `hook` 对象已删除,组件不得再依赖它们监听全局钩子、**必须在对应对象上监听**(残留:`render/ui/main.tsx:146-151` 的 `hook.on('statusBarUpdate', ...)`、`render/index.tsx:35-38` 的 `hook.on('restart', ...)`,及 `main.tsx:140-143` 的手工单次调用);② 加载系统重构,旧加载 UI(`render/ui/load.tsx` 的 `LoadScene` / `LoadSceneUI`)已不能用,仍调用 `loader.initSystemLoadTask()` / `loader.load()` / `loader.progress`(`:71,72,76-79,91-92,118-119,139`),须重新适配新加载系统(`@motajs/loader` 的 `LoadManager` + `packages-user/data-state/src/loader/**` 的 `MotaDataLoader` / `IMotaDataLoader`)。**D-72**:本目标至少两个计划——第一个只读收集(生产代码零改动),第二个及后续才执行;第一个计划的产出是后续执行计划的输入。**D-73**:沿用 D-33 / D-42 / D-48 / D-49 / D-55 / D-65 / D-56 / D-68,**D-69 桶导出边界继续适用**。**交付物** = `.planning/phases/04-render-adaptation/04-UI-ADAPTATION-IMPACT.md`(只读影响台账):元信息块 + `只读起始基线` / `背景` / `方法` / `基准变更(d8fa4b8)` / A 全局 loading / hook 使用点(UI 面)/ B 目标对象与钩子 API 证据 / C 旧加载 UI 清单与新加载系统 / D 分层与 barrel(D-69)检查 / E legacy 命中(仅报告)/ F 未能从阅读确定(未猜测)/ 修改点汇总(供后续执行计划消费)/ 处置;A–E 固定列序表 `| ID | 项目 | 命中处 file:line | 目标锚点 / 证据 file:line | 问题描述 | 影响 | 置信 |`,事实行每行 ≥2 个 `file:line` 锚点、证据行带 `[证据行]` 标记;每个 A 点给出「应改监听 / 使用的对象」+ 现行源码证据,或显式 undetermined(进 F 类)。**边界**:被查 = 渲染端部分 UI(`client-modules/src/render/ui/**` + `render/index.tsx`);`client.ts` / `client-modules/src/index.ts` / `entry-client/src/create.ts` 的 `loading` / `hook` 使用点**只报告**(非 UI,是否纳入由用户裁定);`packages/` 与 `src/` 仅报告(D-33)。**门禁**:只读基线逐条一致 + 台账结构 / 内容 / 证据纪律(≥2 锚点)+ 范围与用户改动 + 既有 `04-01`..`04-12` 产物保护 + CRLF + 人工复核(**不设 `check:type` / `build` / TS 诊断数类门禁**,D-68 的延续);`autonomous: false`(Task 0 汇报关卡等待用户「可以执行」);**本步为只读、不引入任何命名变更**;`REND-01` / `REND-02` 仍 Pending。 +**Plan register note (2026-09-30 · 04-14 第二步):** 第十四个增量 `04-14` = **部分 UI 的进一步接口适配·第二步·适配实施(代码改动)**(用户裁定 2026-09-30 · **D-74 / D-75 / D-76 / D-77 / D-78**)。**要解决的问题**:`04-13` 只读台账确认,数据端 `d8fa4b8 refactor: Delete global loading & hook object` 与加载系统重构后,渲染端部分 UI 仍以已删全局 `loading` / `hook` 与旧加载接口编写。**D-74..D-78 逐条落地**:① **D-78(tracer)** `render/ui/main.tsx` 删除全局 `hook` 状态栏监听残留(`:145-152`)与手工单次调用垫片(`:140-143`),改为在**勇士属性对象** `client.hero.attribute` 上 `addHook({ onUpdateAttribute })` 并在 `onUnmounted` 注销(接口 `packages-user/data-base/src/hero/types.ts:95-106`;触发点 `packages-user/data-base/src/hero/attribute.ts:89`),新增局部常量 `attributeHook`;② **D-74 / D-75** `render/ui/load.tsx` 以 `client.loader.start()`(`AsyncIterable`)取代旧「系统加载任务初始化 + 加载 + `progress` 迭代」三件套,进度 / 字节 / 任务读数一律取自 `client.loader.manager`(`ILoadManager`)的 `getLoadedTasks` / `getAddedTasks` / `getTotalByte` / `getLoadedByte` / `getByteRatio`,并移除 `:75` 失效 `@ts-expect-error`;③ **D-76** 删除 `render/index.tsx:35-38` 被注释 `restart` 监听块;④ **D-77** 删除 `client.ts:88-92,124-127` 两段被注释兼容层(含「兼容层」标签)与 `entry-client/src/create.ts:16,31,34-41` 被注释接线及模块创建函数块(`client-modules/src/index.ts` 经 `git grep` 实证**零残留、不编辑**)。**沿用约束** D-33 / D-42 / D-48 / D-49 / D-55 / D-56 / D-65 / D-68 / **D-69(本步零新增转发、不改任何 barrel)**。**开点(未裁定、默认不处置)**:旧加载 UI 的装配 / 可达性 —— `createGameRenderer`(`render/index.tsx:13`)无调用者、`LoadSceneUI` 仅在 `:25` 被 `sceneController.open`,本计划默认**不重新接线**,须用户另行裁定。**命名**:仅新增局部常量 `attributeHook`(Task 0 获批);不新增 / 改名任何公共 / 受保护 / 私有成员。**门禁**:编辑集最终形状 + 残留清零 + 范围(`material/` / `packages/` / `src/` / 数据端 / `packages/common` 零改动)+ 用户改动保留(`packages/common/src/{hook.ts,types.ts}` 与 `04-CONTEXT.md`,D-56)+ 白名单提交 + 既有 `04-01`..`04-13` 产物保护 + CRLF + 人工复核(**不设 `check:type` / `build` / TS 诊断数类门禁**,D-68 的延续);`autonomous: false`(Task 0 汇报关卡等待用户「可以执行」);无依赖变更、不执行 `pnpm i`;`REND-01` / `REND-02` 仍 Pending。 **Plan register note (2026-09-23):** 第九个增量 `04-09` = TextureManager 新接口适应**第二步(实施·代码改动)**(用户裁定 D-43..D-50:`renderWithoutCheck` 三处调用点改新 `render(tile, connection)`;`BlockCls` / `cls` 判定改为 `tileType === TileType.Autotile`、不做映射表;`extension/hero.ts` 勇士字面量填 `tileType: TileType.Unknown`;`MapRenderer` 不再自建 `AutotileProcessor` 而取 `manager.autotile`、保留 `readonly autotile` 字段与 `IMapRenderer` 签名;不改 `material/**` 与加载面;不考虑 legacy 兼容)。对象 = `packages-user/client-modules/src/render/map/{vertex.ts,renderer.ts,extension/hero.ts}` 的 **13 条** texture 归属 TS 诊断(vertex 6 / renderer 5 / hero 2),目标归零;`moving.ts` 与 `render/map/types.ts` 经核实无需改动。**已规划待执行**,执行前须 Task 0 汇报关卡等待用户「可以执行」;`REND-01` / `REND-02` 仍 Pending(本步仅为 REND-01 的 texture 子切片)。 Plans: @@ -239,6 +240,10 @@ Plans: - [x] 04-13-PLAN.md — 部分 UI 的进一步接口适配·**第一步·只读收集需要修改的点(只读,D-71 / D-72 / D-73)**:以 `d8fa4b8 refactor: Delete global loading & hook object` 后的工作树为基准,收集渲染端**部分 UI** 对已删全局 `loading` / `hook` 与旧加载系统的使用点,产出 `04-UI-ADAPTATION-IMPACT.md`(元信息块 + `只读起始基线` / `背景` / `方法` / `基准变更(d8fa4b8)` / A 全局 loading / hook 使用点(UI 面)/ B 目标对象与钩子 API 证据 / C 旧加载 UI 清单与新加载系统 / D 分层与 barrel(D-69)检查 / E legacy 命中(仅报告)/ F 未能从阅读确定(未猜测)/ 修改点汇总 / 处置;A 类 = `render/ui/load.tsx` 旧加载接口(`:71,72,76-79,91-92,118-119,139`)+ `render/ui/main.tsx` `statusBarUpdate`(`:140-143,146-151`)+ `render/index.tsx` `restart`(`:35-38`),边界非 UI 面(`client.ts:88-92,124-127` / `client-modules/src/index.ts` / `entry-client/src/create.ts:16,31,34-41`)只报告;B 类逐处给目标对象证据(`IMotaDataLoader` / `ILoadManager` / 数据端可 hook 对象接口)或 undetermined;C 类登记旧 `LoadScene` / `LoadSceneUI` 与已删 `client-base/src/load/**` + 新 `@motajs/loader` / `data-state/src/loader/**` / `CoreState.loader` / `loadManager`;D 类 4 个 barrel 节点(只报告);E 类 legacy 只报告;F 类 ≥ 5 条未确定项;事实行每行 ≥2 个 `file:line` 锚点),**本步生产代码零改动、不引入任何命名变更**;门禁 = 只读基线一致 + 台账结构 / 内容 / 证据纪律 + 范围与用户改动 + 既有 `04-01`..`04-12` 产物保护 + CRLF + 人工复核(**不设 `check:type` / `build` / TS 诊断数类门禁**,D-68 的延续);`autonomous: false`(Task 0 汇报关卡等待用户「可以执行」);第二步(适配实施)待用户审阅后另行规划(D-72);`REND-01` / `REND-02` 仍 Pending +**Wave 13** *(blocked on Wave 12 completion)* + +- [ ] 04-14-PLAN.md — 部分 UI 的进一步接口适配·**第二步·适配实施(代码改动,D-74 / D-75 / D-76 / D-77 / D-78)**:`render/ui/main.tsx` 状态栏改在勇士属性对象 `client.hero.attribute` 上注册 `onUpdateAttribute` 钩子(tracer,删除全局 `hook` 残留与手工调用垫片,新增局部 `attributeHook`);`render/ui/load.tsx` 改依 `client.loader.start()` + `client.loader.manager.get*`;删除 `render/index.tsx:35-38` 的 `restart` 注释块与 `client.ts:88-92,124-127` / `entry-client/src/create.ts:16,31,34-41` 的非 UI 残留(`client-modules/src/index.ts` 零残留不编辑);默认不重新接线旧加载 UI 装配点(开点,待用户裁定);`autonomous: false`(Task 0 汇报关卡等待用户「可以执行」);门禁全静态(不设 `check:type` / `build` / TS 诊断数类门禁,D-68 的延续);`REND-01` / `REND-02` 仍 Pending + **UI hint**: yes ### Phase 5: Legacy 移植 diff --git a/.planning/STATE.md b/.planning/STATE.md index b991375..ac9d306 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -5,14 +5,14 @@ current_phase: 04 current_phase_name: 渲染适配与双布局 status: executing stopped_at: Completed 04-13-PLAN.md(部分 UI 进一步接口适配·第一步只读收集:交付 04-UI-ADAPTATION-IMPACT.md;A7/B5/C2/D5/E4/F5 + 8 行修改点汇总;A-01 目标可确定=IMotaDataLoader/ILoadManager,其余多 undetermined;生产代码零改动;REND-01/REND-02 保持 Pending,第二步未规划) -last_updated: "2026-09-30T07:10:00.000Z" +last_updated: "2026-09-30T08:10:04.336Z" last_activity: 2026-09-30 -last_activity_desc: Completed 04-13-PLAN.md(部分 UI 进一步接口适配·第一步只读收集) -state_head: 26e2fb7 +last_activity_desc: Planned 04-14-PLAN.md(部分 UI 进一步接口适配·第二步适配实施) +state_head: 425ff2829785decfa6dcfdfb072e9f68aae2135f progress: total_phases: 7 completed_phases: 1 - total_plans: 84 + total_plans: 85 completed_plans: 83 milestone_name: milestone --- @@ -28,10 +28,10 @@ See: .planning/PROJECT.md (updated 2026-09-17) ## Current Position -Phase: 04 (渲染适配与双布局) — IN PROGRESS -Plan: 13/13 已执行(04-01..04-13) -Status: 04-13 完成(部分 UI 适配·第一步只读收集);下一步 = 第二步适配实施(待你审阅台账后另行规划,D-72);Phase 4 整体仍未完成 — REND-01/REND-02 保持 Pending -Last activity: 2026-09-30 — Completed 04-13-PLAN.md(部分 UI 进一步接口适配·第一步只读收集) +Phase: 04 (渲染适配与双布局) — READY TO EXECUTE +Plan: 13/14 已执行(04-01..04-13);04-14(部分 UI 进一步接口适配·第二步适配实施)已规划待执行(Wave 13) +Status: 04-14 已规划并通过 checker(0 blocker / 0 warning / 4 info-advisory);待用户回复「可以执行」后执行;Phase 4 整体仍未完成 — REND-01/REND-02 保持 Pending +Last activity: 2026-09-30 — Planned 04-14-PLAN.md(部分 UI 进一步接口适配·第二步适配实施) Progress: [█████░░░░░] 50% diff --git a/.planning/phases/04-render-adaptation/04-14-PLAN.md b/.planning/phases/04-render-adaptation/04-14-PLAN.md new file mode 100644 index 0000000..a757f3f --- /dev/null +++ b/.planning/phases/04-render-adaptation/04-14-PLAN.md @@ -0,0 +1,519 @@ +--- +phase: 04-render-adaptation +plan: 14 +type: execute +wave: 13 +depends_on: + - 04-01 + - 04-02 + - 04-03 + - 04-04 + - 04-05 + - 04-06 + - 04-07 + - 04-08 + - 04-09 + - 04-10 + - 04-11 + - 04-12 + - 04-13 +files_modified: + - packages-user/client-modules/src/render/ui/main.tsx + - packages-user/client-modules/src/render/ui/load.tsx + - packages-user/client-modules/src/render/index.tsx + - packages-user/client-modules/src/client.ts + - packages-user/entry-client/src/create.ts +files_deleted: [] +autonomous: false +requirements: + - REND-01 + - REND-02 +estimate: + tokens: 60000 + raw_tokens: 60000 + tasks: 5 + confidence: low +must_haves: + truths: + - "**本计划的第一个 block 点是 Task 0 的 `blocking-human` 汇报关卡(AGENTS.md 硬要求)**:Task 0 必须先向用户汇报「要解决的问题(数据端 `d8fa4b8 refactor: Delete global loading & hook object` 与加载系统重构后,部分 UI 仍以已删全局 `loading` / `hook` 与旧加载接口编写)」「逐条裁定 **D-74(旧加载 UI 进度数据来源 = 新加载接口提供的方法)/ D-75(旧 `loader.progress` 异步迭代的等价 = 新接口 `start()` 返回的 `AsyncIterable`)/ D-76(`restart` 事件不再需要,删除相关注释内容)/ D-77(三处非 UI 旧接线残留直接删除)/ D-78(状态栏改在勇士属性对象上注册 `onUpdateAttribute` 钩子)**,以及沿用约束 D-33 / D-42 / D-48 / D-49 / D-55 / D-56 / D-65 / D-68 / D-69」「**待批准的精确编辑集**(含将要删除的既有注释行与被删除的手工单次调用垫片)」「**将新增的局部标识符命名**(`main.tsx` 内局部常量 `attributeHook`)」「**开点:旧加载 UI 的装配 / 可达性(`render/index.tsx:11,13,25`)与 `client-modules/src/index.ts` 在 D-77 下无可删残留的事实,D-74..D-78 未裁定**」,然后必须**停下等待**用户明确回复「可以执行」四个字;收到前不得执行任何任务、不得派发任何执行子代理。无论触发方式(`/gsd-execute-phase`、`/gsd-manager`、`/gsd-autonomous`、子代理/执行器派发)或配置模式(`.planning/config.json` 的 `mode: yolo`、`workflow.auto_advance`)如何,均不得自动放行。" + - "**D-78 端到端落地(tracer,`main.tsx`)**:状态栏更新不再依赖已删全局 `hook`——改为在勇士属性对象上注册钩子:`client.hero.attribute.addHook({ onUpdateAttribute })`(`IReadonlyHeroAttribute extends IHookable>`,`packages-user/data-base/src/hero/types.ts:95-106`;触发点 `packages-user/data-base/src/hero/attribute.ts:89`);`main.tsx` 内不再出现任何全局 `hook` 监听 / 注销残留;挂载期手工单次调用垫片(原 `:140-143`)被移除,改为由注册的控制器 `unload()` 在 `onUnmounted` 中清理。" + - "**D-74 / D-75 落地(旧加载 UI 重新适配,`load.tsx`)**:`LoadScene` 改依新加载系统——`const loader = client.loader`(`IMotaDataLoader`)保留;`loader.start()`(`AsyncIterable`)取代旧 `initSystemLoadTask()` + `load()` + `progress` 异步迭代;进度 / 字节 / 任务数据一律取自 `loader.manager`(`ILoadManager`)的现有方法 `getLoadedTasks` / `getAddedTasks` / `getTotalByte` / `getLoadedByte` / `getByteRatio`;`load.tsx` 内不再出现旧接口成员;`:75` 的 `@ts-expect-error 需要重构` 随代码修正一并移除(该注解不再需要)。" + - "**D-76 / D-77 落地(删除旧内容)**:`render/index.tsx` 的被注释 `restart` 监听块被删除(D-76);`client.ts:88-92,124-127` 的被注释 `loading` 兼容层块、`entry-client/src/create.ts:16,31,34-41` 的被注释 `loading` / `hook` 接线与 `createModule` 函数被删除(D-77);`client-modules/src/index.ts` 经 `git grep` 实证**零残留**(无需删除)并以事实登记(D-77 列出该文件但现行文件已无 `loading` / `hook` 文本);生产树内再无 `hook.on` / `hook.off` 全局监听残留。" + - "**范围与边界零外溢**:`packages-user/client-base/src/material/**`(D-42 / D-48)、`packages/**`、`src/**`、任何测试文件、legacy 面(D-49 / D-55 / D-65)**零触碰**;无依赖新增、不改 `package.json` / `pnpm-lock.yaml`、不执行 `pnpm i`;不改写 `04-01`..`04-13` 既有 `*-PLAN.md` / `*-SUMMARY.md`;不产出 `04-15+`;不重新设计任何数据端契约。" + - "**用户并发改动零触碰(D-56)**:`.planning/phases/04-render-adaptation/04-CONTEXT.md`(规划日实测 ` M`)一律不修改、不暂存、不提交;`packages/common/src/hook.ts` 与 `packages/common/src/types.ts` 在本规划会话期间被用户提交(`425ff28 refactor: 删除 hook 的 loadHook 接口`;规划日起测时仍为 ` M`)——**无论其提交与否,本计划只读其现行 `IHookable.addHook` / `IHookController.unload` 形状,绝不修改 / 暂存 / 提交**;白名单提交前以 `git status` 逐条核对暂存面。" + - "**命名先获批(AGENTS.md)**:本步不新增 / 改名 / 删除任何公共、受保护、私有成员、方法、字段;如确需新增标识符,仅限任务已列明的**局部**标识符(`main.tsx` 内局部常量 `attributeHook`),且已在 Task 0 汇报并经用户同意;否则不得自行命名。" + - "**无运行时门禁(D-68 的延续)**:全部验证为**静态**(文件内容断言 + `git status --porcelain` 范围断言 + CRLF + 既有产物保护),每条 `` 都带 ``;**不得**出现任何以「`pnpm check:type` 通过」「`pnpm build` 成功」「TS 诊断数归零」为条件的门禁或验收项;`REND-01` / `REND-02` 在本 run 后仍为 **Pending**,`.planning/REQUIREMENTS.md` 零改动。" + artifacts: + - path: packages-user/client-modules/src/render/ui/main.tsx + provides: "D-78:移除全局 `hook` 状态栏监听残留与手工单次调用垫片,改为在 `client.hero.attribute` 上 `addHook({ onUpdateAttribute })` 注册并在 `onUnmounted` 注销" + - path: packages-user/client-modules/src/render/ui/load.tsx + provides: "D-74 / D-75:旧加载 UI 改依新加载系统(`client.loader.start()` + `client.loader.manager.get*`),移除旧接口成员与失效的 `@ts-expect-error`" + - path: packages-user/client-modules/src/render/index.tsx + provides: "D-76:删除被注释的 `restart` 全局 hook 监听块" + - path: packages-user/client-modules/src/client.ts + provides: "D-77:删除被注释的 `loading` 兼容层残留块" + - path: packages-user/entry-client/src/create.ts + provides: "D-77:删除被注释的 `loading` / `hook` 接线与 `createModule` 函数残留" + - path: .planning/phases/04-render-adaptation/04-14-SUMMARY.md + provides: "本增量执行记录:编辑集 / 门禁实测 / 人工复核 / 无运行时验证声明 / 开点(装配可达性)余留" + - path: .planning/ROADMAP.md + provides: "Phase 4 的 `Wave 13` + `04-14` register note(计划阶段登记)" + key_links: + - "`packages-user/client-modules/src/render/ui/main.tsx:145-152`(被注释的全局状态栏监听块)↔ `packages-user/data-base/src/hero/types.ts:95-106`(`IHeroAttributeHooks.onUpdateAttribute` / `IReadonlyHeroAttribute extends IHookable`)↔ `packages-user/data-base/src/hero/attribute.ts:89`(`forEachHook(hook => hook.onUpdateAttribute?.(name, value))` 触发点)↔ `packages/common/src/types.ts:84` / `packages/common/src/hook.ts:23,44`(`addHook` / `removeHookByController`)——D-78 的端到端接线依据" + - "`packages-user/client-modules/src/render/ui/main.tsx:140-143`(手工单次调用 + 「后续修改后需要删除」注释垫片)↔ 本任务新增的 `addHook` 注册 —— 垫片被其自身注释所述「修改后删除」取代" + - "`packages-user/client-modules/src/render/ui/load.tsx:36,71,72,76-79,91-92,118-119,139`(旧接口 `client.loader` / `initSystemLoadTask` / `load` / `progress.get*`)↔ `packages-user/data-state/src/loader/types.ts:21,26,31`(`IMotaDataLoader.manager` / `start()` / `loaded()`)↔ `packages/loader/src/types.ts:288-335`(`ILoadManager.getLoadedTasks` / `getAddedTasks` / `getTotalByte` / `getLoadedByte` / `getByteRatio`)↔ `packages/loader/src/manager.ts:88-111`(实现)——D-74 / D-75 的改指依据" + - "`packages-user/client-modules/src/render/index.tsx:35-38`(被注释 `restart` 监听)—— D-76 删除对象;`packages-user/client-modules/src/client.ts:88-92,124-127` 与 `packages-user/entry-client/src/create.ts:16,31,34-41` —— D-77 删除对象" + - "`packages-user/client-modules/src/index.ts:1-16`(`create()` 现只 `patchAll()` + `createRender()`,无 `loading` / `hook` 文本)↔ D-77 名单 —— **零残留事实**;与 `render/index.tsx:13`(`createGameRenderer` 无调用者)共同构成本计划的**开点(装配 / 可达性)**" + - "`packages/common/src/hook.ts` / `packages/common/src/types.ts`(用户并发改动,D-56;规划日起测为 ` M`,规划会话期间由用户提交 `425ff28`)—— 本计划只**读**其现行 `IHookable.addHook` / `IHookController.unload` 形状,绝不修改 / 暂存 / 提交" + - "`git status --porcelain -- packages packages-user src`(Task 1 步骤 (0a) 记录;规划日实测为空)↔ Task 1 / 2 / 3 / 4 的范围断言 + `packages/common/src/{hook.ts,types.ts}` 与 `.planning/.../04-CONTEXT.md` 的 ` M` 仍在的断言 —— D-56 与范围零外溢证明" + assumptions: + - "**FLAGGED ASSUMPTION(specless fallback)— REND-01 edge unresolved:** 未提供 REND-01(渲染端适配新数据层接口)的验收边界;本计划**不发明**阈值,只交付「部分 UI 进一步接口适配」的**第二步代码改动**(D-74..D-78)。用户须在推进 REND-01 余下部分前补齐边界判据。" + - "**FLAGGED ASSUMPTION(specless fallback)— REND-02 edge unresolved:** 未提供 REND-02(移动端与桌面端双布局)的验收边界;本计划不涉及双布局,也不发明其验收阈值。" + - "**开点 — 旧加载 UI 装配 / 可达性(D-74..D-78 未裁定):** 规划日 `git grep -n \"createGameRenderer\" -- packages-user packages src` 仅命中 `render/index.tsx:13` 定义行(**无调用者**),`LoadSceneUI` 只在 `createGameRenderer`(`render/index.tsx:25`)内被 `sceneController.open`;`client-modules/src/index.ts` 的 `create()` 已不再调用 `createGameRenderer`(`d8fa4b8` 删除了 `loading.once('coreInit')` 接线)。**本计划默认只做 `load.tsx` 的接口适配(D-74 / D-75),不重新接线装配点、不改 `render/index.tsx:11,13,25`**;若用户要求重新接线,退出本计划、修订后重新汇报(Task 0 的 `rewire` 选项)。" + - "**D-77 对 `packages-user/client-modules/src/index.ts` 的名单与现行事实不一致:** 该文件现行(规划日)**零** `loading` / `hook` 文本(`git grep` 实证);故本计划对该文件**无可删除内容**,仅以 `git grep` 证据登记「零残留」,不做任何编辑。Task 0 必须原样告知用户此事实并征得其同意(若用户指认其它意图,退出本计划、修订后重新汇报)。" + - "**规划日实测行号(须用户在 Task 0 确认):** `main.tsx:140-143`(垫片)+ `:145-152`(被注释监听块);`load.tsx:36,71,72,75,76-79,91-92,118-119,139`;`render/index.tsx:35-38`;`client.ts:88-92,124-127`;`entry-client/src/create.ts:16,31,34-41`。**执行日一律以当日实测按符号名 / 文本检索定位**;任一行号漂移按当日实测改写并在 SUMMARY 说明。" + - "**用户并发改动与 hook API 依赖:** 本计划依赖 `packages/common/src/hook.ts` / `types.ts` 的 `IHookable.addHook` / `IHookController.unload` 形状(规划日起测时为 ` M` 未提交,规划会话期间被用户提交为 `425ff28 refactor: 删除 hook 的 loadHook 接口`;现行工作树已含该 API,`git grep` 实测 `addHook` 与 `removeHookByController` 均在);若执行日该形状变化,按当日实测改写并停下向用户说明,不得臆造替代 API。" + - "**无运行时验证可得(D-68 的延续):** 本步不要求「保证能运行」,不启动渲染、不运行任何运行时用例;承载体为静态门禁 + 人工复核;**不得**以 `pnpm check:type` / `pnpm build` / TS 诊断数冒充门禁。" + - "**`client.ts` 的 `initMapExtensions`(`:137-148`)在删除注释块后无调用者:** 按 D-77 的名单(仅 `:88-92,124-127`)**不列入删除集,予以保留**;此事实在 Task 0 一并告知用户。" + prohibitions: + - "不得在用户明确回复「可以执行」四个字之前执行本计划的任何任务、或派发任何执行子代理(AGENTS.md 的第一个 block 点约束;不因触发方式或 `yolo` / `auto_advance` 模式而放宽)。" + - "不得修改任何既有 jsDoc / 注释文本超出门禁批准的编辑集(AGENTS.md):本计划允许删除的既有注释**仅限**被删除的旧接线块自身携带的注释行(`main.tsx:140-143` 的两行说明、`client.ts` 两处 `// 兼容层` 标签、`entry-client/src/create.ts` 被注释接线与 `createModule` 块、`render/index.tsx:35-38` 的 `restart` 注释块),以及 `load.tsx:75` 随代码修正失效的 `@ts-expect-error 需要重构`;**这些删除项须在 Task 0 逐条列出并经用户同意后方可执行**,其余任何既有注释一律不得改动。" + - "不得新增 / 改名 / 删除任何公共、受保护、私有成员、方法、字段、接口或文件;新增标识符仅限局部常量 `attributeHook`(`main.tsx`),且须经 Task 0 获批。" + - "不得触碰、暂存、提交用户的并发改动(D-56):`.planning/phases/04-render-adaptation/04-CONTEXT.md`(仍为 ` M`)与 `packages/common/src/hook.ts` / `packages/common/src/types.ts`(规划会话期间已被用户提交为 `425ff28`,仍不得再改)一律不修改、不暂存、不提交。" + - "不得触碰 `packages-user/client-base/src/material/**`(D-42 / D-48)、`packages/**`、`src/**`、任何测试文件、任何 legacy 面(D-49 / D-55 / D-65)。" + - "不得改动 `package.json` / `pnpm-lock.yaml` / `tsconfig.json` / 任何 lint 配置;不得新增依赖;不得执行 `pnpm i`。" + - "不得重新接线旧加载 UI 的装配点(`render/index.tsx:11,13,25`)或为 `createGameRenderer` 增加调用者——该点属开点,须用户另行裁定。" + - "不得改写 / 覆盖 / 改号 `04-01`..`04-13` 的既有 `*-PLAN.md` / `*-SUMMARY.md`;不得改写既有 `04-CONTEXT.md` / `04-UI-ADAPTATION-IMPACT.md`;不得产出 `04-15+` 或任何新计划文件。除 `files_modified` 列出的五个生产文件、本 `04-14-PLAN.md`、`04-14-SUMMARY.md` 与 `.planning/ROADMAP.md` 的登记外,不得写任何其它文件(含 `.planning/STATE.md` / `WINDOWS.md` / `REQUIREMENTS.md`)。" + - "不得使用 `import type`(dev.md 项目规则);不得新增、删除或修改任何测试文件与既有测试。" + - "不得把任何门禁建立在「全仓 / 范围内 TS 诊断数」「`pnpm check:type`」「`pnpm build`」之上,也不得因这类检查失败而阻塞交付(D-68)。" + - "不得以「无运行时」为由弱化验证:每条 `` 必须有 ``;确实无法运行的门禁必须显式声明为不可得并降级为静态 + 人工,不得伪造绿。" + - "不得把 `loader.manager` 的 `getTotalByte` 实现细节(`packages/loader/src/manage.ts` 中返回 `this.loaded`)当作缺陷登记或自行修正——数据端语义归用户(D-33 / D-42 精神),本步只做接口适应。" +--- + +Phase 4 第十四个增量(用户裁定 2026-09-30 · **D-74 / D-75 / D-76 / D-77 / D-78**)—— **部分 UI 的进一步接口适配·第二步(适配实施,代码改动)**。 + +**要解决的问题:** + +`04-13` 的第一步只读台账(`.planning/phases/04-render-adaptation/04-UI-ADAPTATION-IMPACT.md`,A7 / B5 / C2 / D5 / E4 / F5 + 8 行修改点汇总)确认:数据端经 `d8fa4b8 refactor: Delete global loading & hook object` 与加载系统重构后,渲染端**部分 UI** 仍以已失效的旧接口编写。用户已对全部 F 类未决项作出裁定(D-74..D-78),本计划据其落地: + +| 裁定 | 内容 | 落点 | +|---|---|---| +| **D-74(F-01)** | 旧加载 UI 的「任务进度 / 字节进度 / 总字节」数据来源 = **新加载接口提供的方法**(`IMotaDataLoader` / `ILoadManager`) | `render/ui/load.tsx` | +| **D-75(F-02)** | 旧 `loader.progress` 异步迭代的等价 = 新加载接口的 **`start()`(返回 `AsyncIterable`)** | `render/ui/load.tsx` | +| **D-76(F-04)** | `restart` 事件不再需要 → 删除相关(被注释)内容 | `render/index.tsx:35-38` | +| **D-77(F-05)** | 三处非 UI 旧加载 / 钩子接线残留**直接删除**(「重构即删旧内容」) | `client.ts:88-92,124-127`、`client-modules/src/index.ts`、`entry-client/src/create.ts:16,31,34-41` | +| **D-78(F-03)** | 状态栏更新改为在**勇士属性对象**(`client.hero.attribute`)上注册钩子 `IHeroAttributeHooks.onUpdateAttribute?(name, value)` | `render/ui/main.tsx:145-152`(+ 移除 `:140-143` 垫片) | + +**沿用约束(D-73 延续):** D-33(范围外一律不改、不追平全部类型错误);D-42 / D-48(`packages-user/client-base/src/material/**` 归用户、AI 不得改);D-49 / D-55 / D-65(不考虑任何 legacy);D-56(不得触碰 / 提交用户并发改动);D-68(不要求「保证能运行」、不得以 `check:type` / `build` / TS 诊断数作为门禁);**D-69(桶导出边界)继续适用**——本步不新增任何转发导出、不改任何 barrel。 + +**本计划相对 04-13 的关系:** 04-13 是第一步只读收集(生产代码零改动);本计划是第二步适配实施(代码改动),输入 = `04-UI-ADAPTATION-IMPACT.md` 的 `## 修改点汇总` + D-74..D-78 的逐条裁定。 + +**任务切分(Task 0 汇报关卡 + Task 1 tracer + 2 个铺开任务 + Task 4 收口):** 落在**单一计划文件**内。 + +- **Task 0** = `checkpoint:decision`(`gate="blocking-human"`):汇报问题 / 裁定 / **精确编辑集**(含将被删除的既有注释行与垫片)/ **将新增的局部命名** / **开点(装配可达性)与 D-77 的 `client-modules/src/index.ts` 零残留事实** / 验证方式,等待「可以执行」。 +- **Task 1(tracer)= D-78 的状态栏属性钩子**这一条线**端到端**落地:UI 组件(`main.tsx`)→ 数据端可 hook 属性对象(`client.hero.attribute`)→ 钩子触发点(`attribute.ts:89`),先用一个真实适配点证明「在对应对象上监听」的接线方式,并移除垫片与全局 hook 残留。 +- **Task 2** = D-74 / D-75:旧加载 UI(`load.tsx`)改依新加载系统(`client.loader.start()` + `client.loader.manager.get*`)。 +- **Task 3** = D-76 / D-77:删除 `restart` 注释块与三处非 UI 残留(`client-modules/src/index.ts` 以零残留证据登记)。 +- **Task 4** = 收口(编辑集静态门禁 + 范围 / 用户改动门禁 + 既有产物保护 + CRLF + 人工复核 + `04-14-SUMMARY.md` + 白名单提交)。 + +## Source Audit(多源覆盖审计) + +| SOURCE | ID | Feature/Requirement | Plan | Status | Notes | +|--------|----|---------------------|------|--------|-------| +| GOAL | — | Phase 4:「渲染端通过新数据层接口驱动,并同时支持移动端与桌面端布局」 | 04-14 | COVERED(增量·代码改动子切片) | 本 run 落地「部分 UI 进一步接口适配」的第二步(D-74..D-78);双布局属后续增量(见两条 FLAGGED ASSUMPTION) | +| REQ | REND-01 | 渲染端适配新数据层接口 | 04-14 | COVERED(UI 切片·代码改动;需求保持 Pending) | 「组件改在对应对象上监听 + 旧加载 UI 改依新加载系统」即 REND-01 的 UI 切片;验收边界未提供 → FLAGGED ASSUMPTION | +| REQ | REND-02 | 渲染端同时支持移动端与桌面端布局 | 04-14 | COVERED(不涉及) | 本 run 范围不含双布局;验收边界未提供 → FLAGGED ASSUMPTION | +| RESEARCH | — | (本阶段无 `04-RESEARCH.md`) | — | N/A | Phase 4 未产出 RESEARCH.md;等价的权威输入 = `04-UI-ADAPTATION-IMPACT.md`(04-13 交付物)+ `04-CONTEXT.md` 的 D-74..D-78 | +| CONTEXT | D-74 | 旧加载 UI 进度数据来源 = 新加载接口方法 | 04-14 | COVERED | Task 2(`loader.manager.get*`) | +| CONTEXT | D-75 | `loader.progress` 异步迭代等价 = `start()`(AsyncIterable) | 04-14 | COVERED | Task 2(`for await (... of loader.start())`) | +| CONTEXT | D-76 | `restart` 不再需要,删除相关注释内容 | 04-14 | COVERED | Task 3(`render/index.tsx:35-38`) | +| CONTEXT | D-77 | 三处非 UI 旧接线残留直接删除 | 04-14 | COVERED | Task 3(`client.ts` / `entry-client/src/create.ts` 删除;`client-modules/src/index.ts` 零残留证据登记) | +| CONTEXT | D-78 | 状态栏改在勇士属性对象上注册 `onUpdateAttribute` | 04-14 | COVERED(tracer) | Task 1 | +| CONTEXT | D-71 / D-72 | 目标:全局 `loading` / `hook` 须改在对应对象上监听;旧加载 UI 须重新适配;两步走(本步 = 第二步) | 04-14 | COVERED | Task 1(D-78)+ Task 2(D-74/75)+ Task 3(D-76/77) | +| CONTEXT | D-73 / D-33 | 范围外一律不改、不追平全部类型错误 | 04-14 | COVERED(边界) | `packages/` 与 `src/` 零改动;无「诊断归零」验收项 | +| CONTEXT | D-42 / D-48 | `client-base/src/material/` 归用户、AI 不得改 | 04-14 | COVERED(禁用路径) | Task 4 范围门禁断言 `material/` porcelain 为空 | +| CONTEXT | D-49 / D-55 / D-65 | 不考虑任何 legacy | 04-14 | COVERED(零触碰) | 无 legacy 任务 / 门禁 / 待办;`core.*` 存量使用不触碰 | +| CONTEXT | D-56 | 用户并发改动零触碰 | 04-14 | COVERED | Task 1 (0a) 记录基线;Task 4 断言用户 `04-CONTEXT.md` 与 `packages/common/src/{hook.ts,types.ts}` 改动仍在且未被暂存 | +| CONTEXT | D-68 | 不要求可运行、不以 TS 诊断数 / `check:type` / `build` 为门禁 | 04-14 | COVERED(延续) | 全部门禁为静态;prohibitions 明列 | +| CONTEXT | D-69 | 桶导出边界原则(硬约束) | 04-14 | COVERED(零新增转发) | 本步不改任何 barrel;Task 4 断言无 `index.ts` / `index.tsx` 被改 | +| LEDGER | `#04-13-A-01` / `B-01` | `load.tsx` 旧加载接口(`:36,71,72,76-79,91-92,118-119,139`)→ `IMotaDataLoader` / `ILoadManager` | 04-14 | COVERED | Task 2 | +| LEDGER | `#04-13-A-03` / `F-03`→D-78 | `main.tsx:140-143,145-152` 全局 hook 监听点 | 04-14 | COVERED | Task 1 | +| LEDGER | `#04-13-A-04` / `F-04`→D-76 | `render/index.tsx:35-38` `restart` 监听点 | 04-14 | COVERED | Task 3 | +| LEDGER | `#04-13-A-05/06/07` / `F-05`→D-77 | 边界非 UI 面 `client.ts` / `client-modules/src/index.ts` / `entry-client/src/create.ts` | 04-14 | COVERED | Task 3 | +| LEDGER | `#04-13-A-02` / `#04-13-C-01` | 装配链不可达(`createGameRenderer` 无调用者) | 04-14 | DEFERRED(开点) | D-74..D-78 未裁定;Task 0 显式呈报,默认不接线(见 assumptions / Task 0 `rewire` 选项) | +| Deferred Ideas | — | 阶段 4 后续步骤(双布局);`packages` 全部、`legacy-plugin-*`、`data-fallback` | — | DEFERRED | 未写入任何任务 | + +## Estimate Basis(估算依据) + +`estimate-calibration` 无历史样本(`factor: 1`、`applied: false`、`sample_count: 0`、`confidence: low`),故 `tokens == raw_tokens` 并如实标注 `confidence: low`。按「执行者实际触及量」估算(`estimate.tasks: 5` = Task 0 汇报关卡 + 4 个工作任务):Task 0 只读汇报并等待用户确认(约 6k,不写文件);Task 1(tracer)起始基线 + `main.tsx` 状态栏属性钩子端到端 + 门禁(约 11k);Task 2 `load.tsx` 改依新加载系统 + 门禁(约 13k);Task 3 D-76 / D-77 删除 + 证据 + 门禁(约 12k);Task 4 收口门禁 + 人工复核 + `04-14-SUMMARY.md` + 白名单提交(约 18k);合计约 60k。**本计划刻意不做多计划拆分**:委托明确要求只产出 `04-14` 一份代码改动计划。 + + + +@C:/Users/13194/.config/opencode/gsd-core/workflows/execute-plan.md +@C:/Users/13194/.config/opencode/gsd-core/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/REQUIREMENTS.md +@.planning/phases/04-render-adaptation/04-CONTEXT.md +@.planning/phases/04-render-adaptation/04-UI-ADAPTATION-IMPACT.md +@.planning/phases/04-render-adaptation/04-13-PLAN.md +@.planning/phases/04-render-adaptation/04-12-PLAN.md +@AGENTS.md +@dev.md + + + + + + Task 0: 预执行汇报关卡 —— 汇报「部分 UI 的进一步接口适配·第二步(适配实施)」的问题、D-74..D-78 逐条裁定、**精确编辑集(含将删除的既有注释行与手工调用垫片)**、**将新增的局部命名**、**开点(旧加载 UI 装配 / 可达性)与 D-77 对 `client-modules/src/index.ts` 的零残留事实**、范围与硬边界、验证方式,等待用户回复「可以执行」 + 用户裁定 D-74 / D-75 / D-76 / D-77 / D-78 已写入 `.planning/phases/04-render-adaptation/04-CONTEXT.md`(以文件内实际存在为准);`04-13` 的第一步只读台账 `.planning/phases/04-render-adaptation/04-UI-ADAPTATION-IMPACT.md` 已在场(含 `## 修改点汇总`);`client-modules/src/index.ts` 现行不含 `loading` / `hook` 文本。 + (只读汇报,不修改任何文件) + + - AGENTS.md(「任何计划的执行,其第一个 block 点必须是向我汇报计划内容并等待我确认」;只有明确回复「可以执行」四个字之后才允许开始执行;「任何公共、受保护、私有方法、字段等内容的命名必须先向我反馈」;「不得擅自修改所有 jsDoc 注释,不得擅自修改任何已有注释」;「除非我明确要求,否则任何修改必须经由 GSD 标准流程」) + - .planning/phases/04-render-adaptation/04-CONTEXT.md(**D-74 / D-75 / D-76 / D-77 / D-78 逐条裁定原文**;D-71 / D-72 / D-73;D-33 / D-42 / D-48 / D-49 / D-55 / D-65 / D-56 / D-68 / D-69) + - .planning/phases/04-render-adaptation/04-UI-ADAPTATION-IMPACT.md(`## 修改点汇总` 8 行 + `#04-13-A-01..A-07` / `F-01..F-05`) + - .planning/phases/04-render-adaptation/04-13-PLAN.md(上一增量 Task 0 汇报关卡的结构与口径) + - .planning/phases/04-render-adaptation/04-14-PLAN.md(本计划全文:objective 的裁定表、tasks、must_haves、禁令、门禁) + - dev.md(「双端分离」系统层 / 实现层;「其他要求」的 CRLF、不得 `import type`) + + + **停下并向用户汇报,等待明确回复后才继续;汇报本身只读,不写任何文件、不改任何生产代码、不派发子代理。** + + ① **要解决的问题**:`04-13` 只读台账确认,数据端经 `d8fa4b8 refactor: Delete global loading & hook object` 与加载系统重构后,渲染端部分 UI 仍以已失效的旧接口编写。用户对全部 F 类未决项已裁定,本计划据其做**第二步的代码改动**。 + + ② **逐条汇报裁定原文要点(D-74..D-78)**: + - **D-74(F-01)**:旧加载 UI 的「任务进度 / 字节进度 / 总字节」数据来源 = **新加载接口提供的方法**(`IMotaDataLoader.manager` → `ILoadManager` 的 `getLoadedTasks` / `getAddedTasks` / `getTotalByte` / `getLoadedByte` / `getByteRatio`)。 + - **D-75(F-02)**:旧 `loader.progress` 异步迭代的等价 = 新加载接口的 **`start()`(返回 `AsyncIterable`)**。 + - **D-76(F-04)**:`restart` 事件不再需要 → **删除**被注释的相关内容。 + - **D-77(F-05)**:三处**非 UI** 的旧加载 / 钩子接线残留**直接删除**(「重构即删旧内容」,不保留、不适配)。 + - **D-78(F-03)**:状态栏更新改为在**勇士属性对象**(`client.hero.attribute`)上注册钩子 `IHeroAttributeHooks.onUpdateAttribute?(name, value)`(`packages-user/data-base/src/hero/types.ts:95-106`;触发点 `packages-user/data-base/src/hero/attribute.ts:89`)。 + - 沿用约束:D-33 / D-42 / D-48 / D-49 / D-55 / D-65 / D-56 / D-68 / **D-69(桶导出边界,本步零新增转发)**。 + + ③ **精确编辑集(须逐条原样呈报,供用户批准;这是 AGENTS.md「不得擅自修改既有注释」的批准入口)**: + - **A. `packages-user/client-modules/src/render/ui/main.tsx`** —— 新增:在 `MainScene` 内注册 `client.hero.attribute.addHook({ onUpdateAttribute })` 且处理器调用既有 `updateStatus()` 与 `updateDataFallback()`,控制器存为**局部常量 `attributeHook`**,并在既有 `onUnmounted` 内调用 `attributeHook.unload()`;**删除**:`:140-143` 的手工单次调用垫片(含两行既有说明注释「保留函数调用…」与「后续修改后需要删除」,以及 `updateDataFallback();` / `updateStatus();`);**删除**:`:145-152` 中被注释的全局 hook 监听 / 注销行(其上方既有注释「监听状态栏更新事件」按用户意见决定保留或改写)。 + - **B. `packages-user/client-modules/src/render/ui/load.tsx`** —— 改:`startLoad` 以内层 `client.loader.start()` 的异步迭代取代旧的「系统加载任务初始化 + 加载 + progress 迭代」三件套,迭代体内从 `client.loader.manager` 的现有方法取任务数 / 字节数,迭代结束后调用既有 `loadEnd()`;改:`renderTaskList` / `renderByteList` 中的旧 `progress` 成员访问改为 `client.loader.manager` 的对应方法;**删除**:`:75` 的失效 `@ts-expect-error`(其理由文本为「需要重构」,代码修正后不再需要);**保留** `const loader = client.loader;` 与既有 `loadEnd`。 + - **C. `packages-user/client-modules/src/render/index.tsx`** —— **删除**:`:35-38` 被注释的 `restart` 监听块(D-76)。 + - **D. `packages-user/client-modules/src/client.ts`** —— **删除**:`:88-92` 与 `:124-127` 两段被注释的兼容层块(含两处既有「兼容层」标签);**保留**:`:137-148` 的 `initMapExtensions`(注:删除上述注释后它已无调用者,但不在 D-77 的名单内,故不删)。 + - **E. `packages-user/entry-client/src/create.ts`** —— **删除**:`:16`、`:31` 两行被注释接线,以及 `:34-41` 被注释的模块创建函数块。 + - **F. `packages-user/client-modules/src/index.ts`** —— **无可删除内容**:该文件现行零残留(无任何被删全局对象的文本);D-77 虽列出该文件,但 `d8fa4b8` 已删除其接线,故本计划仅以核对命令登记「零残留」事实,**不做任何编辑**。若用户认为该文件另有意图(例如应删除 `createGameRenderer` 或重新接线),请选 `revise` 并说明。 + - **将新增的标识符(命名预批)**:仅一个**局部常量 `attributeHook`**(`main.tsx` 的 `MainScene` 内);不新增 / 改名 / 删除任何公共、受保护、私有成员、方法、字段、接口或文件。 + + ④ **开点(D-74..D-78 未裁定,须原样呈报;本计划默认不处置)**:旧加载 UI 的**装配 / 可达性**——规划日 `createGameRenderer`(`packages-user/client-modules/src/render/index.tsx:13`)无调用者,`LoadSceneUI` 仅在 `:25` 被 `sceneController.open`;`client-modules/src/index.ts` 的 `create()` 已不调用 `createGameRenderer`。**本计划默认只做 `load.tsx` 的接口适配(D-74 / D-75),不重新接线装配点、不改 `render/index.tsx:11,13,25`**。如用户要求一并重新接线,请在关卡中选择相应选项,本计划将退出并修订后重新汇报。 + + ⑤ **范围与硬边界(须原样呈报)**:改动面 = 上列五个生产文件;**零触碰** = `packages-user/client-base/src/material/**`(D-42 / D-48)、`packages/**`、`src/**`、任何测试文件、任何 barrel(`index.ts` / `index.tsx` 的导出面,D-69)、任何 legacy 面(D-49 / D-55 / D-65)、用户并发改动 `packages/common/src/{hook.ts,types.ts}` 与 `.planning/phases/04-render-adaptation/04-CONTEXT.md`(D-56)。**无依赖变更、不改 `package.json` / `pnpm-lock.yaml`、不执行 `pnpm i`;不得使用 `import type`。** + + ⑥ **验证方式(须原样呈报,并声明本步无运行时要求)**:D-68 的延续——本步**没有「保证能运行」的要求**。证明方式全部为**静态**:① 各任务的**文件内容断言**(目标文件含新接线形状、不含被删旧形状);② **范围门禁**:`git status --porcelain -- packages-user/client-base/src/material packages packages/ src` 为空;③ **用户改动门禁**:`packages/common/src/{hook.ts,types.ts}` 与 `04-CONTEXT.md` 的 ` M` 仍在、未被暂存;④ **既有产物保护**:`04-01`..`04-13` 的 `*-PLAN.md` / `*-SUMMARY.md` porcelain 为空、无 `04-15+`;⑤ **CRLF**:被编辑的生产文件与 `04-14-SUMMARY.md`;⑥ **人工复核**。**不得使用** `pnpm check:type` / `pnpm build` / TS 诊断数。 + + ⑦ **编号与 wave(须原样告知)**:本计划为 **04-14**(第十四个增量),`wave: 13`,`depends_on` 列 `04-01`..`04-13`;既有 `04-01`..`04-13` 产物一律不得改写 / 覆盖 / 改号,也不得产出 `04-15+`。估算 **60k tokens**(`estimate.tasks: 5`)。 + + ⑧ **关卡性质与失败语义(必须原样告知用户)**:无论触发方式(`/gsd-execute-phase`、`/gsd-manager`、`/gsd-autonomous`、子代理 / 执行器派发)或配置模式(`mode: yolo`、`workflow.auto_advance`)如何,**执行者 / 子代理都必须在此停下并等待**用户明确回复「可以执行」四个字;未收到前不得执行任何任务、不得派发执行子代理,也不得以「自动化模式」为由自动放行。用户未回复「可以执行」即继续,或回复的是其它内容(含修订意见、沉默、自动化模式下的自动推进),一律视为**未通过本关卡**。 + + ⑨ **若用户给出修订意见**(含要求重新接线装配点 / 不同意某条删除 / 要求保留手工单次调用 / 反对局部命名 `attributeHook`):退出本计划、修订 `04-14-PLAN.md` 后重新汇报,不得自行改道执行。 + + 是否按本计划(04-14:部分 UI 进一步接口适配·第二步·适配实施——D-78 状态栏属性钩子(tracer)+ D-74/D-75 旧加载 UI 改依新加载系统 + D-76 restart 注释删除 + D-77 三处非 UI 残留删除,改动五个生产文件;默认不重新接线旧加载 UI 装配点)开始执行 + + AGENTS.md 要求任何计划的第一个 block 点必须是向用户汇报计划内容并等待用户确认。`.planning/config.json` 为 `mode: yolo`,故该关卡必须显式声明为 `blocking-human` 并置 `autonomous: false`。本计划含三类必须让用户明确知晓的判断:① **会删除既有注释行**(`main.tsx:140-143` 的两行说明、两处「兼容层」标签、`restart` / `createModule` 注释块、`load.tsx:75` 的失效 `@ts-expect-error`)——AGENTS.md 禁止擅自修改既有注释,故须在关卡获批;② **会移除手工单次调用垫片**(状态栏初始填充方式随之改变)——影响行为,须用户知晓;③ **一条开点未裁定**(旧加载 UI 装配 / 可达性)与 **一条名单 / 事实差异**(D-77 列出 `client-modules/src/index.ts` 但该文件现行零残留)。规划日起测的用户并发未提交改动为 `packages/common/src/hook.ts`、`packages/common/src/types.ts`、`.planning/phases/04-render-adaptation/04-CONTEXT.md`(均 ` M`);规划会话期间用户已将前两者提交为 `425ff28 refactor: 删除 hook 的 loadHook 接口`,`04-CONTEXT.md` 仍为 ` M`。本计划对三者一律不触碰、不暂存、不提交。 + + + + + + + 回复「可以执行」四个字(修订意见请按 action ⑨ 处理) + + 用户在对话中明确回复「可以执行」四个字(人工核对用户回复原文;本关卡为人工关卡,无自动命令,失败语义写在 action ⑧ 与 acceptance_criteria 中) + + + - 用户回复中明确包含「可以执行」四个字 + - 汇报涵盖:要解决的问题、**D-74..D-78 逐条裁定要点**与沿用约束、**精确编辑集 A..F**(含将删除的既有注释行与手工调用垫片)、**将新增的局部命名 `attributeHook`**、**开点(旧加载 UI 装配 / 可达性)**与 **D-77 `client-modules/src/index.ts` 零残留事实**、范围与硬边界、验证方式 + - 汇报中已原样告知 **D-68 的延续**(没有「保证能运行」的要求;不设 `check:type` / `build` / TS 诊断数类门禁) + - 汇报中已原样告知 **编号与 wave**(04-14 / wave 13 / `depends_on` 04-01..04-13)以及既有 `04-01`..`04-13` 产物不得改写 / 覆盖 / 改号、不得产出 `04-15+` + - 汇报中已原样告知**关卡性质**:执行者 / 子代理必须停下等待,不因触发方式或 `yolo` / `auto_advance` 模式自动放行 + + 用户明确回复「可以执行」;在此之前未执行任何任务、未修改任何文件、未派发任何执行子代理 + + + + Task 1(tracer):一条线端到端 —— 状态栏更新改在勇士属性对象上注册钩子(D-78):`main.tsx` UI 组件 → `client.hero.attribute`(`IHookable`)→ `onUpdateAttribute` 触发点,同时移除全局 hook 残留与手工调用垫片 + Task 0 已闭环:用户在对话中明确回复「可以执行」四个字(未收到则不得开始本任务);且用户已批准编辑集 A(含 `:140-143` 垫片与 `:145-152` 注释块的删除)与局部命名 `attributeHook`。 + packages-user/client-modules/src/render/ui/main.tsx + + - packages-user/client-modules/src/render/ui/main.tsx(整文件 314 行:`:8` `import { defineComponent, onUnmounted, reactive, ref } from 'vue'`;`:32` `import { client } from '../../core'`;`:96-133` `updateStatus`;`:135-138` `updateDataFallback`;`:140-143` 手工单次调用垫片 + 两行既有说明注释;`:145-152` 被注释的全局 hook 监听 / 注销块——**本任务的全部改动面**) + - packages-user/data-base/src/hero/types.ts:95-106(`IHeroAttributeHooks` 的 `onUpdateAttribute?(name: K, value: THero[K]): void`;`IReadonlyHeroAttribute extends IHookable>`)与 :921-932(`IHeroState.attribute: IReadonlyHeroAttribute`) + - packages-user/data-base/src/hero/attribute.ts:63-90(`recalculateAttribute` 内 `:89` `this.forEachHook(hook => hook.onUpdateAttribute?.(name, value))`——触发点) + - packages/common/src/types.ts:76-104(`IHookable.addHook(hook: Partial): C` / `removeHookByController(hook: C): void`)与 :44-60(`IHookController.unload(): void`)与 packages/common/src/hook.ts:23-32,63-72(`addHook` 实现 / `HookController.unload`) + - packages-user/data-state/src/core.ts:89,145-147(`readonly hero: IHeroState` 装配)与 packages-user/client-modules/src/client.ts:41(`ClientCore extends CoreState`) + - AGENTS.md(不得擅自修改既有注释;命名需先获批)与 dev.md(CRLF;不得 `import type`) + + + (0) **复验前置(只读,不改任何文件)**:`git grep -n "createGameRenderer" -- packages-user packages src`(规划日仅命中 `render/index.tsx:13`);`git status --porcelain -- packages-user/client-modules/src/render/ui/main.tsx`(本任务开始前应为空);确认 `packages/common/src/{hook.ts,types.ts}` 的 `IHookable.addHook` / `IHookController.unload` 形状仍如 read_first 所列——若已变化,**停下向用户说明,不得臆造替代 API**。 + + (1) **替换状态栏监听接线(D-78,tracer 的正题)**:把 `main.tsx` 中被注释的全局 hook 监听 / 注销块所在区域(`:145-152`)改为在勇士属性对象上注册钩子: + - 取对象:`client.hero`(`CoreState.hero`)→ `client.hero.attribute`(`IReadonlyHeroAttribute`,`IHookable>`); + - 注册:`const attributeHook = client.hero.attribute.addHook({ onUpdateAttribute: () => { updateStatus(); updateDataFallback(); } });`(局部常量名 **`attributeHook`**,已经 Task 0 获批); + - 注销:在既有 `onUnmounted` 回调内调用 `attributeHook.unload()`(`IHookController.unload` → `removeHookByController`),替换原被注释的全局 hook 注销行; + - 注意:`onUpdateAttribute` 的参数名是属性名、值是属性值(`attribute.ts:89` 处 `name` / `value`);本接线**无需**消费这两个参数即可照常更新状态栏(处理器按现值整体刷新),**不得**为此新增任何接口或改动数据端。 + - 既有注释 `// 监听状态栏更新事件`(`:145`)**保留**(不得改动既有注释);如需为新代码加说明注释,新增即可。 + + (2) **移除手工单次调用垫片(`:140-143`)**:删除该区块的两行既有说明注释(「保留函数调用,避免被识别为未使用函数而报错」与「后续修改后需要删除」)与 `updateDataFallback();`、`updateStatus();` 两行——注册钩子后两函数均被使用,垫片按其自身注释所述「修改后删除」不再需要。**这是本任务被批准删除的仅有两处既有注释。** + + (3) **确认零残留**:`main.tsx` 内不得再出现全局 hook 的监听 / 注销调用,也不得再出现全局 hook 标识符(`attributeHook.unload()` 内的 `Hook.` 为局部常量,不是全局 hook 引用)。`updateStatus` / `updateDataFallback` 的函数体(`:96-138`)**一律不动**(其中的 `core.*` 存量使用属 legacy,D-49 / D-55 / D-65 不管)。 + + (4) **CRLF 与收口**:确认 `main.tsx` 换行为 CRLF;本任务只允许编辑 `main.tsx`,**不得**改任何其它文件、不得新增计划文件、不得派发会写文件的子代理;**不得**执行 `pnpm check:type` / `pnpm build`。 + + 本任务只改一个 UI 组件的钩子接线(在已存在的 `IHookable` 对象上注册 / 注销),无契约 / 命名 / 依赖变更,可用 `git revert` 或重跑本任务回退。 + + node -e "const fs=require('fs');const f='packages-user/client-modules/src/render/ui/main.tsx';const t=fs.readFileSync(f,'utf8');const need=['client.hero.attribute','addHook(','onUpdateAttribute','attributeHook','unload()'];const miss=need.filter(k=>t.indexOf(k)<0);if(miss.length>0){console.error('D78 WIRING MISSING :: '+miss.join(' | '));process.exit(1)}console.log('OK D-78 attribute hook wired');" + non-zero exit, 或 stderr 含 "D78 WIRING MISSING"(`main.tsx` 未在 `client.hero.attribute` 上注册 `onUpdateAttribute` 钩子,或未保留控制器 / 未注销) + node -e "const fs=require('fs');const f='packages-user/client-modules/src/render/ui/main.tsx';const t=fs.readFileSync(f,'utf8');const bad=[];if(t.indexOf('hook.on(')>=0)bad.push('GLOBAL HOOK LISTEN LEFT');if(t.indexOf('hook.off(')>=0)bad.push('GLOBAL HOOK OFF LEFT');if(t.indexOf('\u540e\u7eed\u4fee\u6539\u540e\u9700\u8981\u5220\u9664')>=0)bad.push('SHIM COMMENT LEFT');if(bad.length>0){console.error('RESIDUE LEFT :: '+bad.join(' ; '));process.exit(1)}console.log('OK no global hook residue, no shim');" + non-zero exit, 或 stderr 含 "RESIDUE LEFT" / "GLOBAL HOOK LISTEN LEFT" / "GLOBAL HOOK OFF LEFT" / "SHIM COMMENT LEFT"(全局 hook 监听 / 注销残留未清,或手工调用垫片及其说明注释仍在) + node -e "const{execSync}=require('child_process');const o=execSync('git status --porcelain -- packages-user/client-base/src/material packages packages/ src packages-user/data-base packages-user/data-state packages/loader packages/common',{encoding:'utf8'}).replace(/[ \t\r\n]+$/,'');if(o.length>0){console.error('FORBIDDEN/MATERIAL/DATA TREE TOUCHED :: '+o);process.exit(1)}console.log('OK forbidden paths clean');" + non-zero exit, 或 stderr 含 "FORBIDDEN/MATERIAL/DATA TREE TOUCHED"(触碰了 `material/` / `packages/` / `src/` / 数据端 / `packages/common` —— 违反 D-42 / D-48 / D-33 / D-56) + node -e "const fs=require('fs');const f='packages-user/client-modules/src/render/ui/main.tsx';const s=fs.readFileSync(f,'utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf){console.error('NON-CRLF '+f);process.exit(1)}console.log('OK CRLF main.tsx');" + non-zero exit, 或 stderr 含 "NON-CRLF"(被编辑文件出现非 CRLF 换行,违反 dev.md) + + + - `main.tsx` 在 `client.hero.attribute` 上以 `addHook({ onUpdateAttribute: ... })` 注册钩子,控制器存为局部常量 `attributeHook`,并在 `onUnmounted` 中 `unload()` + - `main.tsx` 内不再出现全局 hook 的监听 / 注销调用;`:140-143` 的手工单次调用垫片及其两行既有说明注释已移除 + - `updateStatus` / `updateDataFallback` 的函数体未被改动;`client.hero.attribute.getFinalAttribute(...)` 等既有读取保持 + - 未触碰 `material/` / `packages/` / `src/` / 数据端 / `packages/common`;`main.tsx` 为 CRLF;未执行 `check:type` / `build` + + 端到端一条线走通:状态栏更新已从「全局 hook 监听」改为「在勇士属性对象上注册 `onUpdateAttribute` 钩子」并在卸载时注销;垫片与全局 hook 残留清除;门禁(接线 / 残留 / 禁用路径 / CRLF)全绿;后续任务只需按各自裁定横向落地其余改动 + + + + Task 2:旧加载 UI 改依新加载系统(D-74 / D-75)—— `load.tsx` 以 `client.loader.start()` 的 `AsyncIterable` 取代旧加载三件套,进度数据一律取自 `client.loader.manager` 的既有方法 + Task 0 已闭环(用户已回复「可以执行」)且用户已批准编辑集 B(含 `:75` 失效 `@ts-expect-error` 的删除)。 + packages-user/client-modules/src/render/ui/load.tsx + + - packages-user/client-modules/src/render/ui/load.tsx(整文件 165 行:`:36` `const loader = client.loader;`;`:63-68` `loadEnd`;`:70-83` `startLoad`(`:71` 旧系统加载任务初始化调用、`:72` 旧加载调用 + `.then(loadEnd)`、`:75` 失效 `@ts-expect-error`、`:76-79` 旧 `progress` 异步迭代与 `getLoadedTasks` / `getLoadedByte`);`:86-110` `renderTaskList`(`:91-92`);`:112-145` `renderByteList`(`:118-119,139`)——**本任务的全部改动面**) + - packages-user/data-state/src/loader/types.ts:19-31,84-142(`IMotaDataLoader`:`readonly manager: ILoadManager`、`start(): AsyncIterable`、`loaded(): Promise`;`MotaDataLoader.start()` 三段 `yield* this.manager.load()`) + - packages/loader/src/types.ts:288-335(`ILoadManager`:`getLoadedByte` / `getTotalByte` / `getByteRatio` / `getLoadedTasks` / `getAddedTasks` / `getTaskRatio`)与 packages/loader/src/manager.ts:88-111(实现) + - packages-user/data-state/src/types.ts:29(`ICoreState.loader: IMotaDataLoader`)与 packages-user/client-modules/src/client.ts:131(`this.loader.addCoreConfig('client', config.clientURL)`) + - AGENTS.md(不得擅自修改既有注释)与 dev.md(CRLF;不得 `import type`) + + + (0) **复验前置(只读)**:确认 `client.loader` 类型为 `IMotaDataLoader`(`packages-user/data-state/src/types.ts:29`)且 `IMotaDataLoader` 上存在 `start()` 与 `manager`(`packages-user/data-state/src/loader/types.ts:21,26`);确认 `start()` 的迭代值语义为加载进度数值、迭代结束即全部加载完成,故**迭代结束后**调用既有 `loadEnd()`(原 `.then(loadEnd)` 的等价)。 + + (1) **重写 `startLoad`(`:70-83`)**:删除旧的系统加载任务初始化调用(`:71`)与旧的加载调用 + `.then(loadEnd)`(`:72`);改为 `for await (const _ of loader.start()) { taskProgress.set(loader.manager.getLoadedTasks()); byteProgress.set(loader.manager.getLoadedByte()); }`,循环后调用 `loadEnd()`。删除 `:75` 的失效 `@ts-expect-error`(其理由文本为「需要重构」——代码修正后该注解不再需要;**这是本任务唯一被批准的既有注释删除**)。循环变量沿用原形态 `_`(不引入新命名)。 + + (2) **`renderTaskList`(`:86-110`)**:把旧的 `progress` 成员访问(`:91` 的任务数、`:92` 的已添加任务数)改为 `loader.manager` 的对应方法(`getLoadedTasks` / `getAddedTasks`);其余绘制逻辑与既有注释**一律不动**。 + + (3) **`renderByteList`(`:112-145`)**:把旧的 `progress` 成员访问(`:118` 的总字节、`:119` 的已加载字节、`:139` 的字节比)改为 `loader.manager` 的对应方法(`getTotalByte` / `getLoadedByte` / `getByteRatio`);其余绘制逻辑与既有注释**一律不动**。 + + (4) **不得做的事**:不得改动数据端(`packages/loader/**`、`packages-user/data-state/**`);不得因 `LoadManager.getTotalByte` 实现返回 `this.loaded` 而自行「修正」语义(数据端语义归用户);不得为 `loader.manager` 的取值另行缓存 / 包装 / 新增成员;不得使用 `import type`;不得触碰 `render/index.tsx`(装配点属开点,另见 Task 0)。 + + (5) **CRLF 与收口**:确认 `load.tsx` 为 CRLF;本任务只允许编辑 `load.tsx`,**不得**改任何其它文件、不得新增计划文件、不得派发会写文件的子代理;**不得**执行 `pnpm check:type` / `pnpm build`。 + + 本任务只把 UI 的加载读数来源从已删旧接口改到新加载接口的现有方法,无契约 / 命名 / 依赖变更,可用 `git revert` 或重跑本任务回退。 + + node -e "const fs=require('fs');const f='packages-user/client-modules/src/render/ui/load.tsx';const t=fs.readFileSync(f,'utf8');const need=['client.loader','loader.start()','loader.manager.getLoadedTasks','loader.manager.getAddedTasks','loader.manager.getTotalByte','loader.manager.getLoadedByte','loader.manager.getByteRatio','loadEnd()'];const miss=need.filter(k=>t.indexOf(k)<0);if(miss.length>0){console.error('LOADER ADAPT MISSING :: '+miss.join(' | '));process.exit(1)}console.log('OK load UI adapted to new loader');" + non-zero exit, 或 stderr 含 "LOADER ADAPT MISSING"(`load.tsx` 未以 `start()` + `manager.get*` 完成改指,或 `client.loader` / `loadEnd()` 丢失) + node -e "const fs=require('fs');const f='packages-user/client-modules/src/render/ui/load.tsx';const t=fs.readFileSync(f,'utf8');const bad=[];if(t.indexOf('loader.progress')>=0)bad.push('OLD PROGRESS MEMBER LEFT');if(t.indexOf('initSystemLoadTask')>=0)bad.push('OLD INIT LEFT');if(t.indexOf('@ts-expect-error \u9700\u8981\u91cd\u6784')>=0)bad.push('STALE TS-EXPECT-ERROR LEFT');if(bad.length>0){console.error('OLD INTERFACE RESIDUE :: '+bad.join(' ; '));process.exit(1)}console.log('OK no old loader interface residue');" + non-zero exit, 或 stderr 含 "OLD INTERFACE RESIDUE" / "OLD PROGRESS MEMBER LEFT" / "OLD INIT LEFT" / "STALE TS-EXPECT-ERROR LEFT"(旧加载接口成员或失效 `@ts-expect-error` 仍在) + node -e "const{execSync}=require('child_process');const o=execSync('git status --porcelain -- packages-user/client-base/src/material packages packages/ src packages-user/data-state packages/loader packages-user/data-base packages/common',{encoding:'utf8'}).replace(/[ \t\r\n]+$/,'');if(o.length>0){console.error('FORBIDDEN PATH TOUCHED :: '+o);process.exit(1)}console.log('OK forbidden paths clean');" + non-zero exit, 或 stderr 含 "FORBIDDEN PATH TOUCHED"(触碰 `material/` / `packages/` / `src/` / 数据端 / `packages/common` —— 违反 D-42 / D-48 / D-33 / D-56) + node -e "const fs=require('fs');const f='packages-user/client-modules/src/render/ui/load.tsx';const s=fs.readFileSync(f,'utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf){console.error('NON-CRLF '+f);process.exit(1)}console.log('OK CRLF load.tsx');" + non-zero exit, 或 stderr 含 "NON-CRLF"(`load.tsx` 出现非 CRLF 换行) + + + - `load.tsx` 以 `client.loader.start()` 的异步迭代取代旧的系统加载任务初始化 + 加载调用 + `progress` 迭代三件套,循环后调用 `loadEnd()` + - `renderTaskList` / `renderByteList` 的进度读数一律取自 `client.loader.manager` 的既有方法;不再出现旧接口成员 + - `:75` 的失效 `@ts-expect-error`(理由「需要重构」)已移除;其余既有注释与绘制逻辑未改动 + - 未触碰数据端 / `material/` / `packages/` / `src/` / `packages/common`;`load.tsx` 为 CRLF;未执行 `check:type` / `build` + + 旧加载 UI 已按 D-74 / D-75 重新适配:进度 / 字节 / 任务数据全部来自新加载接口的现有方法,异步迭代走 `start()`,旧接口成员与失效 `@ts-expect-error` 清零;门禁(改指 / 残留 / 禁用路径 / CRLF)全绿 + + + + Task 3:删除旧内容(D-76 / D-77)—— `render/index.tsx` 的 `restart` 注释块、`client.ts` 的两段被注释兼容层、`entry-client/src/create.ts` 的被注释接线与模块创建函数块;并以核对命令登记 `client-modules/src/index.ts` 零残留 + Task 0 已闭环(用户已回复「可以执行」)且用户已批准编辑集 C / D / E(含 `restart` 注释块、两处「兼容层」标签、被注释接线与模块创建函数块的删除)。 + packages-user/client-modules/src/render/index.tsx, packages-user/client-modules/src/client.ts, packages-user/entry-client/src/create.ts + + - packages-user/client-modules/src/render/index.tsx(整文件 49 行:`:29-41` `createRender` 内 `:35-38` 被注释的 `restart` 监听块——**删除对象 C**;`:43-49` 7 条同目录 `export *` **一律不动**) + - packages-user/client-modules/src/client.ts(整文件 149 行:`:88-92` 与 `:124-127` 两段被注释的兼容层块(含两处「兼容层」标签)——**删除对象 D**;`:137-148` `initMapExtensions` **保留**;`:39,60,131` 新加载系统接线 **一律不动**) + - packages-user/entry-client/src/create.ts(整文件 41 行:`:16`、`:31` 两行被注释接线与 `:34-41` 被注释的模块创建函数块——**删除对象 E**;`:18-29` 的 `Mota.register(...)` 与 `:1-13` 的 import **一律不动**) + - packages-user/client-modules/src/index.ts(16 行:`create()` 现只 `patchAll()` + `createRender()`;规划日实测无任何被删全局对象的文本——**零残留证据对象 F,不编辑**) + - .planning/phases/04-render-adaptation/04-CONTEXT.md(D-76 / D-77 裁定原文)与 04-UI-ADAPTATION-IMPACT.md 的 `#04-13-A-04` / `#04-13-A-05/06/07` + - AGENTS.md(不得擅自修改既有注释)与 dev.md(CRLF;不得 `import type`) + + + (0) **复验前置(只读)**:`git grep -n "createGameRenderer" -- packages-user packages src`(记录当日结果,供 SUMMARY 开点事实);逐文件确认待删块的现行行号与形态(执行日以当日实测为准)。 + + (1) **删除对象 C(D-76)**:删除 `packages-user/client-modules/src/render/index.tsx` 中被注释的 `restart` 监听块(`:35-38`;该块原为「全局 hook 监听 → `sceneController.closeAll()` + 开启标题界面」)。**不得**改动 `createRender` 的其余调用、`:43-49` 的导出面、任何 import。删除后的 `createRender` 不得留下空注释占位。 + + (2) **删除对象 D(D-77)**:删除 `packages-user/client-modules/src/client.ts` 中 `:88-92` 与 `:124-127` 两段被注释的兼容层块,**连同两处既有的「兼容层」标签注释**(标签所指内容整体被删,标签本身即残留的一部分)。**保留** `:137-148` 的 `initMapExtensions`(不在名单内,虽已无调用者亦不删);`this.loader.addCoreConfig('client', config.clientURL)`(`:131`)与其余构造逻辑 **一律不动**。 + + (3) **删除对象 E(D-77)**:删除 `packages-user/entry-client/src/create.ts` 中 `:16`、`:31` 两行被注释接线,以及 `:34-41` 被注释的模块创建函数块。**保留**全部 import 与 `:18-29` 的 `Mota.register(...)`;删除后 `create()` 只保留注册逻辑,不得留下空注释占位。 + + (4) **零残留证据 F(D-77)**:对 `packages-user/client-modules/src/index.ts` 执行核对命令 `git grep -n -E "hook\.|loading" -- packages-user/client-modules/src/index.ts`,**原样记录输出**(规划日实测为空,退出码 1);本计划对该文件**不做任何编辑**,并在 SUMMARY 中登记「D-77 名单 / 现行事实差异 + 零残留证据」。 + + (5) **全局零残留复核**:执行 `git grep -n -E "hook\.on\(|hook\.off\(" -- packages-user/client-modules/src packages-user/entry-client/src`,断言为空(`render/use.ts` 内的 `hook` 是函数参数名,不属全局对象,不在命中范围)。 + + (6) **CRLF 与收口**:确认三个被编辑文件为 CRLF;本任务只允许编辑上述三个文件,**不得**改任何其它文件、不得新增计划文件、不得派发会写文件的子代理;**不得**触碰 barrel 导出面(D-69);**不得**执行 `pnpm check:type` / `pnpm build`。 + + 本任务仅删除已失效的被注释旧接线(连同其标签注释),无契约 / 命名 / 依赖变更,可用 `git revert` 或重跑本任务回退。 + + node -e "const fs=require('fs');const t=fs.readFileSync('packages-user/client-modules/src/render/index.tsx','utf8');const bad=[];if(t.indexOf('// hook.')>=0)bad.push('RESTART COMMENT LEFT');if(t.indexOf('export function createRender')<0)bad.push('createRender MISSING');if(t.indexOf(\"'./ui'\")<0)bad.push('BARREL EXPORT DAMAGED');if(bad.length>0){console.error('D76 GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK D-76 restart comment removed');" + non-zero exit, 或 stderr 含 "D76 GATE FAILED" / "RESTART COMMENT LEFT" / "createRender MISSING" / "BARREL EXPORT DAMAGED"(`restart` 注释块未删、误删 `createRender`,或导出面被破坏 —— 违反 D-76 / D-69) + node -e "const fs=require('fs');const t=fs.readFileSync('packages-user/client-modules/src/client.ts','utf8');const bad=[];if(t.indexOf('// loading.')>=0)bad.push('CLIENT COMPAT BLOCK LEFT');if(t.indexOf('class ClientCore')<0)bad.push('ClientCore MISSING');if(t.indexOf('initMapExtensions')<0)bad.push('initMapExtensions LOST');if(bad.length>0){console.error('D77-CLIENT GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK D-77 client.ts compat blocks removed');" + non-zero exit, 或 stderr 含 "D77-CLIENT GATE FAILED" / "CLIENT COMPAT BLOCK LEFT" / "ClientCore MISSING" / "initMapExtensions LOST"(`client.ts` 兼容层注释块未删、误删类定义,或误删不在名单内的 `initMapExtensions`) + node -e "const fs=require('fs');const t=fs.readFileSync('packages-user/entry-client/src/create.ts','utf8');const bad=[];if(t.indexOf('// loading.')>=0)bad.push('CREATE LOADING LEFT');if(t.indexOf('// async function')>=0)bad.push('CREATEMODULE BLOCK LEFT');if(t.indexOf('export function create')<0)bad.push('create MISSING');if(t.indexOf(\"Mota.register('@user/client-modules'\")<0)bad.push('REGISTER DAMAGED');if(bad.length>0){console.error('D77-CREATE GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK D-77 entry-client/create.ts residues removed');" + non-zero exit, 或 stderr 含 "D77-CREATE GATE FAILED" / "CREATE LOADING LEFT" / "CREATEMODULE BLOCK LEFT" / "create MISSING" / "REGISTER DAMAGED"(`create.ts` 注释残留未清、误删 `create` 或 `Mota.register` 面) + node -e "const{execSync}=require('child_process');let status=0,out='';try{out=execSync('git grep -n -E \"hook\\\\.on\\\\(|hook\\\\.off\\\\(\" -- packages-user/client-modules/src packages-user/entry-client/src',{encoding:'utf8'})}catch(e){status=(typeof e.status==='number')?e.status:1;out=String(e.stdout||'')}if(status!==0&&status!==1){console.error('GIT GREP FAILED status='+status);process.exit(1)}if(status===0&&out.trim().length>0){console.error('GLOBAL HOOK RESIDUE :: '+out.trim());process.exit(1)}console.log('OK zero global hook call sites across client-modules + entry-client (git status='+status+')');" + non-zero exit, 或 stderr 含 "GLOBAL HOOK RESIDUE" / "GIT GREP FAILED"(`client-modules` / `entry-client` 源码面仍有全局 hook 监听 / 注销调用 —— D-76 / D-77 未清干净;或核对命令本身未正常执行) + node -e "const{execSync}=require('child_process');const o=execSync('git status --porcelain -- packages-user/client-base/src/material packages packages/ src packages-user/data-state packages-user/data-base packages/common',{encoding:'utf8'}).replace(/[ \t\r\n]+$/,'');if(o.length>0){console.error('FORBIDDEN PATH TOUCHED :: '+o);process.exit(1)}const files=['packages-user/client-modules/src/render/index.tsx','packages-user/client-modules/src/client.ts','packages-user/entry-client/src/create.ts'];const bad=[];for(const f of files){const s=require('fs').readFileSync(f,'utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf)bad.push('NON-CRLF '+f)}if(bad.length>0){console.error('CRLF FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK forbidden paths clean + CRLF');" + non-zero exit, 或 stderr 含 "FORBIDDEN PATH TOUCHED" / "CRLF FAILED" / "NON-CRLF"(触碰禁用路径,或被编辑文件出现非 CRLF 换行) + + + - `render/index.tsx` 的 `restart` 注释块已删除,`createRender` 与 `:43-49` 导出面完好(D-76) + - `client.ts` 的两段被注释兼容层块(含两处「兼容层」标签)已删除;`initMapExtensions` 与新加载接线保留(D-77) + - `entry-client/src/create.ts` 的被注释接线与模块创建函数块已删除;import 与 `Mota.register(...)` 面完好(D-77) + - `client-modules/src/index.ts` 经核对命令实证零残留且**未被编辑**(D-77 名单 / 事实差异已登记) + - `client-modules` / `entry-client` 源码面零全局 hook 监听 / 注销调用;未触碰禁用路径;被编辑文件为 CRLF + + 旧内容删除完成:`restart` 注释块、两段兼容层注释、`create.ts` 的被注释接线与模块创建函数块清零,`client-modules/src/index.ts` 零残留已取证;barrel 导出面与保留项(`initMapExtensions` / `Mota.register`)未受损;门禁(D-76 / D-77 / 全局零残留 / 禁用路径 / CRLF)全绿 + + + + Task 4:收口 —— 编辑集与残留静态门禁 + 范围 / 数据端 / 用户改动门禁 + 既有产物保护 + CRLF + 人工复核 + `04-14-SUMMARY.md` + 白名单提交 + Task 0 已闭环(用户已回复「可以执行」)且 Task 1 / 2 / 3 均已交付(D-78 接线 + D-74/D-75 改动 + D-76/D-77 删除)。 + packages-user/client-modules/src/render/ui/main.tsx, packages-user/client-modules/src/render/ui/load.tsx, packages-user/client-modules/src/render/index.tsx, packages-user/client-modules/src/client.ts, packages-user/entry-client/src/create.ts, .planning/phases/04-render-adaptation/04-14-SUMMARY.md + + - .planning/phases/04-render-adaptation/04-12-SUMMARY.md 与 04-11-SUMMARY.md(代码改动增量的 SUMMARY 版式:编辑集 / 门禁实测 / 人工复核 / 无运行时验证声明 / Known Stubs / Next Phase Readiness) + - .planning/phases/04-render-adaptation/04-14-PLAN.md(本计划的 must_haves(truths / artifacts / key_links / assumptions / prohibitions)与逐条门禁) + - .planning/ROADMAP.md(Phase 4 的 `**Plans:**` 行、`**Wave 13**` 条目与 04-14 register note —— 计划阶段登记;本任务不重复改 ROADMAP,除非计划阶段未登记) + - packages-user/client-modules/src/render/ui/main.tsx(Task 1 成品)与 load.tsx(Task 2 成品)与 render/index.tsx / client.ts(Task 3 成品)与 packages-user/entry-client/src/create.ts(Task 3 成品) + - dev.md(「其他要求」的 CRLF;不得 `import type`) + - AGENTS.md(提交前核对暂存面;不得触碰用户并发改动) + + + (1) **编辑集静态门禁(合并复核五个生产文件的最终形状)**:断言 `main.tsx` 含 `client.hero.attribute` / `addHook(` / `onUpdateAttribute` / `attributeHook` / `unload()`;断言 `load.tsx` 含 `loader.start()` 与 `loader.manager.get*` 五个方法名且不含旧接口成员;断言 `render/index.tsx` / `client.ts` / `create.ts` 不含被删注释形状。**不得**执行 `pnpm check:type` / `pnpm build` / 任何 TS 诊断统计。 + + (2) **范围门禁**:断言 `git status --porcelain -- packages-user/client-base/src/material packages packages/ src ` 为空;断言本 run 触及的生产文件**仅**为 `files_modified` 列出的五个(`git status --porcelain -- packages-user/client-modules packages-user/entry-client` 的条目集合 ⊆ 五个;提交后为空亦通过);断言 `.planning/REQUIREMENTS.md` porcelain 为空。 + + (3) **数据端与用户改动门禁(D-56 / D-33)**:断言 `packages-user/data-state` / `packages-user/data-base` / `packages/loader` / `packages/common` porcelain 为空(本 run 未触碰);断言用户并发改动仍在且未被回退 —— `.planning/phases/04-render-adaptation/04-CONTEXT.md` 含 `D-74` 与 `D-78`、`packages/common/src/types.ts` 含 `addHook` 与 `IHookController`、`packages/common/src/hook.ts` 含 `removeHookByController`。 + + (4) **既有产物保护门禁**:断言 `.planning/phases/04-render-adaptation/` 下 `04-01`..`04-13` 的 `*-PLAN.md` / `*-SUMMARY.md` porcelain 为空;断言目录内不存在 `04-15+` 计划 / SUMMARY 文件。 + + (5) **CRLF 门禁**:断言五个被编辑生产文件与 `04-14-SUMMARY.md` 均为 CRLF。 + + (6) **人工复核(不可自动化,如实记录结论)**:逐条打开当日源码核对 —— ① `main.tsx` 的 `addHook({ onUpdateAttribute })` 挂在 `client.hero.attribute`(`IHookable`)上,处理器调用 `updateStatus` / `updateDataFallback`,`onUnmounted` 调 `attributeHook.unload()`;② `load.tsx` 的每个 `loader.manager.get*` 调用与 `ILoadManager`(`packages/loader/src/types.ts:288-335`)成员一一对应,`start()` 的迭代结束即加载完成;③ 三个删除文件的保留项(`createRender`、`initMapExtensions`、`Mota.register(...)`、导出面)完好;④ `client-modules/src/index.ts` 零残留核对命令输出与登记一致。发现偏差即回到相应任务修正,并把结论写入 SUMMARY。 + + (7) **写 `04-14-SUMMARY.md`**:按 04-11 / 04-12 的代码改动增量版式写 —— frontmatter(`phase` / `plan: 14` / `tags` / `requires` / `provides` / `affects` / `actuals` / `key-files`(created / modified / deleted)/ `key-decisions` / `requirements-completed: []` / `duration` / `completed` / `status`)+ 「要解决的问题」+ 编辑集(A..F 逐条,含删除项与新增局部命名)+ 门禁实测结果表 + 人工复核结论 + Decisions Made + Deviations(如有)+ **Known Stubs / 开点(旧加载 UI 装配 / 可达性未接线;`client-modules/src/index.ts` 名单 / 事实差异;`client.ts` 的 `initMapExtensions` 删除注释后无调用者)** + 无运行时验证声明(D-68 的延续)+ Next Phase Readiness(`REND-01` / `REND-02` 保持 Pending)。 + + (8) **白名单提交(单次)**:核对 `git status`,**只**暂存 `files_modified` 列出的五个生产文件与 `04-14-SUMMARY.md`(若 `.planning/ROADMAP.md` 的 04-14 登记在计划阶段未完成,则一并只暂存 `.planning/ROADMAP.md`)——**绝不**暂存 `packages/common/src/hook.ts`、`packages/common/src/types.ts`、`.planning/phases/04-render-adaptation/04-CONTEXT.md` 或任何其它文件;提交信息形如 `feat(04-14): 部分 UI 进一步接口适配·第二步(D-74..D-78)`。**不得**借提交顺带格式化 / 重排无关文件;**不得** amend 任何既有提交。 + + (9) **不写其它 `.planning/` 文件**:本任务除 `04-14-SUMMARY.md` 与(如需)`ROADMAP.md` 登记外**不得**写 `.planning/STATE.md` / `WINDOWS.md` / `REQUIREMENTS.md`。 + + 本任务是收口(静态门禁 + 文档 + 一次白名单提交);提交内容为五个生产文件的接口适配与 SUMMARY,可用 `git revert` 整体回退。 + + node -e "const fs=require('fs');const t=fs.readFileSync('packages-user/client-modules/src/render/ui/main.tsx','utf8');const l=fs.readFileSync('packages-user/client-modules/src/render/ui/load.tsx','utf8');const bad=[];['client.hero.attribute','addHook(','onUpdateAttribute','attributeHook','unload()'].forEach(k=>{if(t.indexOf(k)<0)bad.push('main:'+k)});['loader.start()','loader.manager.getLoadedTasks','loader.manager.getAddedTasks','loader.manager.getTotalByte','loader.manager.getLoadedByte','loader.manager.getByteRatio','loadEnd()'].forEach(k=>{if(l.indexOf(k)<0)bad.push('load:'+k)});if(l.indexOf('loader.progress')>=0)bad.push('load:OLD PROGRESS STILL THERE');if(t.indexOf('hook.on(')>=0||t.indexOf('hook.off(')>=0)bad.push('main:GLOBAL HOOK STILL THERE');if(bad.length>0){console.error('EDIT SET GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK edit set: D-78 wiring + D-74/D-75 adaptation');" + non-zero exit, 或 stderr 含 "EDIT SET GATE FAILED" / "main:" / "load:" / "OLD PROGRESS STILL THERE" / "GLOBAL HOOK STILL THERE"(五个生产文件的最终形状与 D-74 / D-75 / D-78 不符) + node -e "const{execSync}=require('child_process');const q=p=>execSync('git status --porcelain -- '+p,{encoding:'utf8'}).replace(/[ \t\r\n]+$/,'');const bad=[];if(q('packages-user/client-base/src/material packages packages/ src').length>0)bad.push('MATERIAL/PKGS/SRC TOUCHED');if(q('packages-user/data-state packages-user/data-base packages/loader packages/common').length>0)bad.push('DATA SIDE TOUCHED');if(q('.planning/REQUIREMENTS.md').length>0)bad.push('REQUIREMENTS TOUCHED');if(bad.length>0){console.error('SCOPE GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK scope: material/packages/src + data side + REQUIREMENTS untouched');" + non-zero exit, 或 stderr 含 "SCOPE GATE FAILED" / "MATERIAL/PKGS/SRC TOUCHED" / "DATA SIDE TOUCHED" / "REQUIREMENTS TOUCHED"(触碰禁用路径 / 数据端 / 需求文件 —— 违反 D-42 / D-48 / D-33 / D-56) + node -e "const fs=require('fs');const bad=[];const ctx=fs.readFileSync('.planning/phases/04-render-adaptation/04-CONTEXT.md','utf8');if(ctx.indexOf('D-74')<0||ctx.indexOf('D-78')<0)bad.push('USER CONTEXT EDIT LOST');const ty=fs.readFileSync('packages/common/src/types.ts','utf8');if(ty.indexOf('addHook')<0||ty.indexOf('IHookController')<0)bad.push('USER COMMON/TYPES EDIT LOST');const hk=fs.readFileSync('packages/common/src/hook.ts','utf8');if(hk.indexOf('removeHookByController')<0)bad.push('USER COMMON/HOOK EDIT LOST');if(bad.length>0){console.error('USER EDITS GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK user concurrent edits preserved');" + non-zero exit, 或 stderr 含 "USER EDITS GATE FAILED" / "USER CONTEXT EDIT LOST" / "USER COMMON"(用户并发改动被回退 / 覆盖 —— 违反 D-56) + node -e "const{execSync}=require('child_process');const subj=execSync('git log -1 --pretty=format:%s',{encoding:'utf8'}).trim();const bad=[];if(subj.indexOf('04-14')<0)bad.push('LAST COMMIT NOT 04-14 :: '+subj);const names=execSync('git log -1 --name-only --pretty=format:',{encoding:'utf8'}).split(/\r?\n/).filter(Boolean);const allow=new Set(['packages-user/client-modules/src/render/ui/main.tsx','packages-user/client-modules/src/render/ui/load.tsx','packages-user/client-modules/src/render/index.tsx','packages-user/client-modules/src/client.ts','packages-user/entry-client/src/create.ts','.planning/phases/04-render-adaptation/04-14-SUMMARY.md','.planning/phases/04-render-adaptation/04-14-PLAN.md','.planning/ROADMAP.md']);const extra=names.filter(n=>!allow.has(n));if(extra.length>0)bad.push('NON-WHITELIST STAGED :: '+extra.join(' ; '));if(bad.length>0){console.error('WHITELIST COMMIT GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK whitelist commit only');" + non-zero exit, 或 stderr 含 "WHITELIST COMMIT GATE FAILED" / "LAST COMMIT NOT 04-14" / "NON-WHITELIST STAGED"(提交混入用户并发改动或白名单外文件) + node -e "const fs=require('fs');const path=require('path');const dir='.planning/phases/04-render-adaptation';const files=fs.readdirSync(dir);const newPlan=files.filter(f=>/^04-(1[5-9]|[2-9][0-9])-/.test(f));const bad=[];if(newPlan.length>0)bad.push('NEW PLAN BEYOND 04-14 :: '+newPlan.join(','));const summ='.planning/phases/04-render-adaptation/04-14-SUMMARY.md';if(!fs.existsSync(summ))bad.push('SUMMARY MISSING');else{const s=fs.readFileSync(summ,'utf8');['04-14','D-74','D-75','D-76','D-77','D-78','Pending'].forEach(k=>{if(s.indexOf(k)<0)bad.push('SUMMARY MISSING '+k)});const lf=(s.match(/\n/g)||[]).length,crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf)bad.push('SUMMARY NON-CRLF')}if(bad.length>0){console.error('CLOSURE GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK closure: no 04-15+, SUMMARY present and keyed');" + non-zero exit, 或 stderr 含 "CLOSURE GATE FAILED" / "NEW PLAN BEYOND 04-14" / "SUMMARY MISSING" / "SUMMARY NON-CRLF"(产出了 `04-15+`,或 `04-14-SUMMARY.md` 缺失 / 缺关键标识 / 非 CRLF) + node -e "const fs=require('fs');const files=['packages-user/client-modules/src/render/ui/main.tsx','packages-user/client-modules/src/render/ui/load.tsx','packages-user/client-modules/src/render/index.tsx','packages-user/client-modules/src/client.ts','packages-user/entry-client/src/create.ts'];const bad=[];for(const f of files){const s=fs.readFileSync(f,'utf8');const lf=(s.match(/\n/g)||[]).length,crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf)bad.push('NON-CRLF '+f)}if(bad.length>0){console.error('CRLF GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK CRLF five edited files');" + non-zero exit, 或 stderr 含 "CRLF GATE FAILED" / "NON-CRLF"(被编辑生产文件出现非 CRLF 换行,违反 dev.md) + node -e "const{execSync}=require('child_process');const o=execSync('git status --porcelain -- .planning/phases/04-render-adaptation',{encoding:'utf8'}).replace(/\s+$/,'');const rows=o.length?o.split(/\r?\n/):[];const existing=rows.filter(r=>/04-(0[1-9]|1[0-3])-(PLAN|SUMMARY)\.md$/.test(r));if(existing.length>0){console.error('ARTIFACT PROTECTION FAILED :: '+existing.join(' ; '));process.exit(1)}console.log('OK existing 04-01..04-13 artifacts untouched');" + non-zero exit, 或 stderr 含 "ARTIFACT PROTECTION FAILED"(改写 / 覆盖了既有 `04-01`..`04-13` 产物) + + + - **编辑集门禁**:五个生产文件最终形状符合 D-74 / D-75 / D-78,D-76 / D-77 删除项清零 + - **范围门禁**:`material/` / `packages/` / `src/` / 数据端 / `packages/common` / `.planning/REQUIREMENTS.md` porcelain 为空;本 run 生产改动 ⊆ `files_modified` + - **用户改动门禁**:`04-CONTEXT.md`(D-74..D-78)与 `packages/common/src/{hook.ts,types.ts}` 的并发改动内容未被回退 / 覆盖(内容判据;两处 common 文件已被用户自行提交为 `425ff28`) + - **白名单提交**:单次提交只含五个生产文件 + `04-14-SUMMARY.md`(及必要的 ROADMAP 登记) + - **既有产物保护**:`04-01`..`04-13` 未被改写;无 `04-15+` + - **CRLF**:五个被编辑文件与 SUMMARY 均为 CRLF + - `04-14-SUMMARY.md` 已写(含编辑集 / 门禁实测 / 人工复核 / 开点 / 无运行时验证声明);未执行 `check:type` / `build`;未写其它 `.planning/` 文件 + + 收口完成:编辑集与残留门禁全绿、范围 / 数据端 / 用户改动门禁通过、既有 `04-01`..`04-13` 产物零触碰、白名单单次提交完成;`04-14-SUMMARY.md` 落盘;开点(旧加载 UI 装配 / 可达性)与名单 / 事实差异如实登记;`REND-01` / `REND-02` 保持 Pending + + + + + +## Trust Boundaries + +| Boundary | Description | +|----------|-------------| +| (无新增外部边界) | 本计划只改渲染端 UI 组件的内部接线(读取数据端已存在的 `IHookable` 对象 / 新加载接口成员),不引入外部输入 / 网络 / 进程边界;不新增依赖、不安装包、不执行 `pnpm i` | +| 仓库工作树 → git 提交 | 用户的并发未提交改动(`packages/common/src/hook.ts`、`packages/common/src/types.ts`、`.planning/.../04-CONTEXT.md`,均 ` M`)与本次改动共处同一工作树;若暂存面把控不当,会把用户改动混入本计划提交(D-56) | +| 本计划 → 数据端契约 | 本步只做渲染端接口适应;若执行者顺带「修正」数据端实现(如 `LoadManager.getTotalByte` 的返回值)或新增数据端接口,会越出 D-33 / D-42 的边界 | +| 既有注释 → 编辑集 | AGENTS.md 禁止擅自修改既有注释;本计划批准的删除项若被扩大(顺手改别的注释 / 重排注释),会破坏用户既有表达 | + +## STRIDE Threat Register + +| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan | +|-----------|----------|-----------|----------|-------------|-----------------| +| T-04-14-01 | Tampering | 数据端 / `material/` / `packages/` / `src/` 越界改动 | high | mitigate | 每个任务的 `` 均含禁用路径 porcelain 断言(`material` / `packages` / `src` / `data-state` / `data-base` / `loader` / `common`);prohibitions 明列;Task 4 范围门禁 | +| T-04-14-02 | Tampering | 提交暂存面(`git add`)混入用户并发改动 | high | mitigate | Task 4 步骤 (8) 明示只暂存五个生产文件 + SUMMARY(及必要 ROADMAP);`` 断言 HEAD 提交名 ⊆ 白名单且提交信息含 `04-14`;prohibitions 明列 D-56 | +| T-04-14-03 | Tampering | 既有注释被超范围修改 | medium | mitigate | Task 0 逐条列出被批准删除的注释行并要求用户同意;Task 4 人工复核保留项完好;prohibitions 明限删除项 | +| T-04-14-04 | Tampering | 既有 `04-01`..`04-13` 产物 / 数据端契约被改写 | medium | mitigate | Task 4 既有产物保护门禁(porcelain + 无 `04-15+`);prohibitions 明禁改写 / 新增数据端接口 | +| T-04-14-05 | Spoofing | 「无运行时」被用来弱化验证(伪造绿) | medium | mitigate | 每条 `` 必带 ``;D-68 明禁 `check:type` / `build` / TS 诊断数类门禁;Task 4 人工复核并写结论 | +| T-04-14-06 | Denial of Service | 引入运行时回归却无测试设施可测(渲染端无测试) | low | accept | 用户明确不要求「保证能运行」(D-68);本步为接口适应,正确性由静态门禁 + 人工复核 + 后续用户实测承担;`REND-01` / `REND-02` 保持 Pending | +| T-04-14-SC | Tampering | npm/pip/cargo 安装 | low | accept | 本计划无任何依赖新增 / 变更、不改 `package.json` / `pnpm-lock.yaml`、不执行 `pnpm i`,故无供应链安装面;`T-04-14-SC` 保留为「本 run 不适用」的显式登记 | + + + +**Static-only(D-68 的延续:不设 `check:type` / `build` / TS 诊断数类门禁)** + +- [ ] Task 0:用户明确回复「可以执行」四个字(`blocking-human` 关卡;含编辑集 / 局部命名 / 开点获批) +- [ ] Task 1 门禁:`main.tsx` 在 `client.hero.attribute` 上注册 `onUpdateAttribute` 钩子 + 局部 `attributeHook` + `unload()`;无全局 hook 残留与垫片;禁用路径 clean;CRLF +- [ ] Task 2 门禁:`load.tsx` 用 `start()` + `loader.manager.get*` 改指;无旧接口成员与失效 `@ts-expect-error`;禁用路径 clean;CRLF +- [ ] Task 3 门禁:`restart` 注释块 / 两段兼容层 / `create.ts` 被注释接线与模块创建块删除;保留项完好;`client-modules` + `entry-client` 零全局 hook 调用;禁用路径 clean;CRLF +- [ ] Task 4 门禁:编辑集最终形状 + 范围 / 数据端 / 用户改动 + 白名单提交 + 既有产物保护 + CRLF + `04-14-SUMMARY.md` 落盘 +- [ ] 人工代码复核结论写入 SUMMARY +- [ ] 开点(旧加载 UI 装配 / 可达性)与 D-77 名单 / 事实差异如实登记;`REND-01` / `REND-02` 保持 Pending + + + +- **D-78 落地(tracer)**:状态栏更新改为在 `client.hero.attribute` 上 `addHook({ onUpdateAttribute })`,并在 `onUnmounted` 注销;`main.tsx` 无全局 hook 残留、无手工调用垫片 +- **D-74 / D-75 落地**:旧加载 UI 的进度 / 字节 / 任务数据全部取自 `client.loader.manager` 的现有方法,异步迭代走 `client.loader.start()`;`load.tsx` 无旧接口成员、无失效 `@ts-expect-error` +- **D-76 / D-77 落地**:`restart` 注释块、`client.ts` 两段兼容层注释、`entry-client/src/create.ts` 被注释接线与模块创建函数块全部删除;`client-modules/src/index.ts` 零残留已取证 +- **范围零外溢**:`material/` / `packages/` / `src/` / 数据端 / `packages/common` / 测试文件 / 任何 barrel 导出面全部零触碰;无依赖变更、不执行 `pnpm i` +- **用户改动保留**:`packages/common/src/{hook.ts,types.ts}` 与 `04-CONTEXT.md` 的并发改动未被修改 / 暂存 / 提交(D-56) +- **命名受控**:只新增局部常量 `attributeHook`(Task 0 获批);不新增 / 改名任何公共 / 受保护 / 私有成员 +- **无运行时要求**:全部证明为静态门禁 + 人工复核;`REND-01` / `REND-02` 保持 Pending,`.planning/REQUIREMENTS.md` 零改动 +- **开点如实保留**:旧加载 UI 的装配 / 可达性未被私自接线;`client-modules/src/index.ts` 的名单 / 事实差异已登记待裁 + + +## Artifacts this phase produces + +| Artifact | Kind | Purpose | +|---|---|---| +| `packages-user/client-modules/src/render/ui/main.tsx`(修改) | 生产代码 | D-78:删除全局 hook 状态栏监听残留与手工调用垫片,改为在勇士属性对象上注册 `onUpdateAttribute` 钩子并在卸载时注销 | +| `packages-user/client-modules/src/render/ui/load.tsx`(修改) | 生产代码 | D-74 / D-75:旧加载 UI 改依 `client.loader.start()` + `client.loader.manager.get*`;移除旧接口成员与失效 `@ts-expect-error` | +| `packages-user/client-modules/src/render/index.tsx`(修改) | 生产代码 | D-76:删除被注释的 `restart` 监听块 | +| `packages-user/client-modules/src/client.ts`(修改) | 生产代码 | D-77:删除两段被注释的兼容层残留(含标签) | +| `packages-user/entry-client/src/create.ts`(修改) | 生产代码 | D-77:删除被注释的接线与模块创建函数块 | +| `.planning/phases/04-render-adaptation/04-14-SUMMARY.md`(新增) | 文档 | 编辑集 / 门禁实测 / 人工复核 / 开点 / 无运行时验证声明 | +| `.planning/ROADMAP.md`(Wave 13 + 04-14 register note,计划阶段完成) | 文档 | `04-14` 的增量登记 | +| 未改动(显式保留) | — | `packages-user/client-modules/src/index.ts`(零残留,不编辑)、`packages-user/client-modules/src/render/ui/index.ts` 与其它 barrel、`packages-user/client-base/src/**`(含 `material/**`)、`packages-user/data-base/src/**`、`packages-user/data-state/src/**`、`packages/loader/src/**`、`packages/**`、`src/**`、`packages/common/src/{hook.ts,types.ts}`(用户并发改动)、`.planning/REQUIREMENTS.md` | + + +Create `.planning/phases/04-render-adaptation/04-14-SUMMARY.md` when done + + + diff --git a/.planning/state.json b/.planning/state.json index 6a84b8e..72941da 100644 --- a/.planning/state.json +++ b/.planning/state.json @@ -44,5 +44,5 @@ "label": "Advance to the next step", "reason": "Phase 04 of 7 · executing" }, - "updated_at": "2026-09-30T06:35:14.551Z" + "updated_at": "2026-09-30T08:10:04.411Z" }