From 93ed3115fc82feef993c55858335c8c64ef724cf Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Wed, 23 Sep 2026 14:46:54 +0800 Subject: [PATCH] docs(04): plan TextureManager interface adaptation step 2 (04-09) --- .planning/ROADMAP.md | 9 +- .planning/STATE.md | 18 +- .../phases/04-render-adaptation/04-09-PLAN.md | 404 ++++++++++++++++++ .../phases/04-render-adaptation/04-CONTEXT.md | 13 + .planning/state.json | 2 +- 5 files changed, 434 insertions(+), 12 deletions(-) create mode 100644 .planning/phases/04-render-adaptation/04-09-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 9332433..3f1980b 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -181,8 +181,9 @@ Plans: 3. 移动端(窄屏)布局下,同一场景正常显示且可操作 4. 数据端与渲染端保持双端分离——数据端无 DOM,仍可在 Node 环境跑回放验证 -**Plans**: 8/8 plans executed planned — 04-01..04-07 已执行(04-06 = material 接口影响范围清点·只读;04-07 = material 接口适应实施·第二步);**04-08(TextureManager 新接口影响范围清点·只读,D-38/D-39/D-40/D-41/D-42 第一步)已规划待执行**;渲染适配与移动端/桌面端双布局(REND-01/REND-02)整体仍未完成 +**Plans**: 8 已执行 + 1 已规划待执行(04-09) — 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)已规划待执行**(执行前须 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-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: **Wave 1** @@ -214,6 +215,10 @@ Plans: - [x] 04-08-PLAN.md — TextureManager 新接口影响范围清点(**只读**,D-38/D-39/D-40/D-41/D-42 第一步):以 `d36ea69`(`refactor: 贴图存储方式`)后的 `client-base/src/{types.ts,material/types.ts,material/manager.ts,material/autotile.ts,material/index.ts}` 为基准,清点 `packages-user` 消费者面对改名(`MaterialManager`→`TextureManager` 与构造器)、移除(`BlockCls` 枚举 / `IMaterialFramedData.cls`→`tileType` / `IAutotileProcessor` 三方法合一 / `renderAnimatedWith`→`renderAnimated` / `getIdentifierByAlias` / `getAliasByIdentifier` / `getBlockCls(ByAlias)` / `IClientBase.autotile` / `create()` 与 `createMaterial()`)与新增收紧(`tiles` / `autotile` / `tilesetReserve` / `tilesetUnit` / `getFrameCount` / 四个 `add*` 参数 / `flatten` / `AutotileType` / `IClientCoreConfig` 两个必填字段)的引用,按 A 符号改名与移除 / B 成员与签名变化 / C 地图渲染消费者定位索引 / D 被删且无替代(需用户反馈)/ E 加载相关与添加素材(仅报告,D-39/D-41)/ F 未确定 六类分组,每行精确到符号 + `file:line` 且 ≥2 锚点;产出 `04-TEXTURE-INTERFACE-IMPACT.md`,生产代码零改动(`material/` 按 D-42 只登记),第二步实施待用户审阅后另行规划 +**Wave 8** *(blocked on Wave 7 completion)* + +- [ ] 04-09-PLAN.md — TextureManager 新接口适应实施(**第二步·代码改动**,D-43..D-50):`renderWithoutCheck` 的三处调用点(`render/map/vertex.ts:461`、`render/map/vertex.ts:874`、`render/map/renderer.ts:1253`)改新 `render(tile, connection)` 且不保留跳过检查入口(D-43);自动元件判定与解构(`render/map/renderer.ts:1252` / `:1263`、`render/map/vertex.ts:517` / `:561` / `:872` 与 `:852`)改 `tileType === TileType.Autotile`,不做映射表、不用 `AutotileType` 分类(D-44);`render/map/extension/hero.ts:167` 填 `tileType: TileType.Unknown`(D-45,D-18 不排除该文件);`MapRenderer` 保留 `readonly autotile: IAutotileProcessor` 字段与 `IMapRenderer` 签名、构造器改取 `manager.autotile`、不再自建 `AutotileProcessor`(D-46);只做接口适应不重设计 `textures` / `tileStore` / `tiles`(D-47)、`material/**` 零改动(D-48)、不考虑 legacy 兼容(D-49)、`IBlockIdentifier` 零出现(D-50);`moving.ts` 与 `render/map/types.ts` 无需改动;门禁 = 三文件 texture 归属诊断 13→0 + eslint / prettier / CRLF / 符号零命中 / 基线与禁用路径;`autonomous: false`(Task 0 汇报关卡);`REND-01` / `REND-02` 仍 Pending + **UI hint**: yes ### Phase 5: Legacy 移植 @@ -407,7 +412,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 | 1. 事件系统 | 13/13 | In Progress| | | 2. 寻路系统 | 5/5 | In Progress| | | 3. 数据端完成 | 19/19 | Complete | 2026-09-12 | -| 4. 渲染适配与双布局 | 8/8 | In Progress| | +| 4. 渲染适配与双布局 | 8/9 | In Progress| | | 5. Legacy 移植 | 0/TBD | Not started | - | | 6. 单元测试 | 18/18 | In Progress| | | 7. 数据端缺陷修复 | 15/16 | 暂缓 (Deferred) | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index ea2ec60..4d5bf1f 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -4,15 +4,15 @@ milestone: v1.0 current_phase: 04 current_phase_name: 渲染适配与双布局 status: executing -stopped_at: Completed 04-08-PLAN.md(TextureManager 新接口影响范围清点·只读:交付 04-TEXTURE-INTERFACE-IMPACT.md(11 节 / A13 B8 C8 D9 E7 F8 / 每行双 file:line 锚点);旧名 5 个源码面 0 引用;texture 归属错误 23 不变、总错误 263;生产代码零改动;REND-01/REND-02 保持 Pending,第二步未规划) -last_updated: "2026-09-23T05:20:00.000Z" +stopped_at: "Completed 04-08-PLAN.md(TextureManager 新接口影响范围清点·只读:交付 04-TEXTURE-INTERFACE-IMPACT.md(11 节 / A13 B8 C8 D9 E7 F8 / 每行双 file:line 锚点);旧名 5 个源码面 0 引用;texture 归属错误 23 不变、总错误 263;生产代码零改动;REND-01/REND-02 保持 Pending,第二步未规划)" +last_updated: "2026-09-23T06:46:17.171Z" last_activity: 2026-09-23 -last_activity_desc: Completed 04-08-PLAN.md(TextureManager 新接口影响范围清点·只读) -state_head: e47c34c6b0152217d4f20630d89c5297b5336e80 +last_activity_desc: Planned 04-09-PLAN.md(TextureManager 新接口适应·第二步;通过 checker) +state_head: c225fbe100c71247e4de9d0bff103cad2fabc773 progress: total_phases: 7 completed_phases: 1 - total_plans: 79 + total_plans: 80 completed_plans: 78 milestone_name: milestone --- @@ -28,10 +28,10 @@ See: .planning/PROJECT.md (updated 2026-09-17) ## Current Position -Phase: 04 (渲染适配与双布局) — IN PROGRESS -Plan: 8 of 8 -Status: 04-08 完成(TextureManager 新接口影响范围只读清点);Phase 4 整体未完成 — REND-01/REND-02 仍 Pending,第二步(texture 接口适应实施)待用户审阅后另行规划 -Last activity: 2026-09-23 — Completed 04-08-PLAN.md(TextureManager 新接口影响范围清点·只读) +Phase: 04 (渲染适配与双布局) — READY TO EXECUTE +Plan: 8/9 已执行;04-09(TextureManager 新接口适应·第二步)已规划待执行(Wave 8) +Status: 04-09 已规划并通过 checker(0 blocker / 0 warning / 3 info-advisory);待用户回复「可以执行」后执行;Phase 4 整体仍未完成 — REND-01/REND-02 保持 Pending +Last activity: 2026-09-23 — Planned 04-09-PLAN.md(TextureManager 新接口适应·第二步) Progress: [█████░░░░░] 50% diff --git a/.planning/phases/04-render-adaptation/04-09-PLAN.md b/.planning/phases/04-render-adaptation/04-09-PLAN.md new file mode 100644 index 0000000..7b1c54d --- /dev/null +++ b/.planning/phases/04-render-adaptation/04-09-PLAN.md @@ -0,0 +1,404 @@ +--- +phase: 04-render-adaptation +plan: 9 +type: execute +wave: 8 +depends_on: + - 04-01 + - 04-02 + - 04-03 + - 04-04 + - 04-05 + - 04-06 + - 04-07 + - 04-08 +files_modified: + - packages-user/client-modules/src/render/map/vertex.ts + - packages-user/client-modules/src/render/map/renderer.ts + - packages-user/client-modules/src/render/map/extension/hero.ts +autonomous: false +requirements: + - REND-01 + - REND-02 +estimate: + tokens: 52000 + raw_tokens: 52000 + tasks: 4 + confidence: low +must_haves: + truths: + - "**本计划的第一个 block 点是 Task 0 的 `blocking-human` 汇报关卡(AGENTS.md 硬要求)**:Task 0 必须先向用户汇报「要解决的问题(`d36ea69` 重写贴图存储层后,地图渲染端 13 条 texture 归属 TS 错误未跟上新接口)、大致解决方案(按 D-43..D-50 逐条把消费者改成新接口;对象仅 `render/map/{vertex,renderer,extension/hero}.ts`)、逐条裁定编码、验证方式与范围禁令」,并**同时呈报命名审批结论**(本计划不新增、不改名任何公共 / 受保护 / 私有成员或字段),然后必须**停下等待**用户明确回复「可以执行」四个字;收到前不得执行任何任务、不得派发任何执行子代理。无论触发方式(`/gsd-execute-phase`、`/gsd-manager`、`/gsd-autonomous`、子代理 / 执行器派发)或配置(`mode: yolo`、`workflow.auto_advance`)如何,均不得自动放行。" + - "**范围内自动元件渲染不再经过被删除的「跳过检查」入口,三处调用点全部改调新 `render(tile, connection)`,且不保留、不新增任何「跳过检查」入口(D-43 精确落地)** —— 三处 = `render/map/vertex.ts:461`(`updateAutotile`,`autotile.render(tile, connection)`)、`render/map/vertex.ts:874`(`updateMoving` 动态自动元件分支,`this.renderer.autotile.render(block.texture, 0b0000_0000)`)、`render/map/renderer.ts:1253`(`useTileBackground` 单帧分支,`this.autotile.render(tex, 0b1111_1111)!`);除方法名外调用形态、实参与非空断言一律保持不变。" + - "**范围内自动元件判定不再依赖旧 `BlockCls`,统一由 `tileType === TileType.Autotile`(`TileType` 来自 `@user/data-common`)决定,且不出现 `BlockCls`→`TileType` 映射、不用 `AutotileType` 做布局分类(D-44 精确落地)** —— 五处判定 = `render/map/renderer.ts:1252`、`render/map/renderer.ts:1263`、`render/map/vertex.ts:517`、`render/map/vertex.ts:561`、`render/map/vertex.ts:872`;`render/map/vertex.ts:852` 的对象解构键 `cls` 同步改为 `tileType`(仍为多键解构,符合 dev.md 解构规范)。" + - "**勇士贴图字面量不再携带旧 `cls` 成员,改以 `tileType: TileType.Unknown` 构造,其 `BlockCls` import 移除、`TileType` 并入既有的 `@user/data-common` 导入块(D-45 精确落地)** —— 落点 `render/map/extension/hero.ts:167`;该文件是本步工作项(D-18 的排除只针对数据层 `HeroRendering`,不覆盖渲染端 `extension/hero.ts`)。" + - "**`MapRenderer` 不再自建自动元件处理器,其 `autotile` 直接取自 `manager.autotile`,字段声明与接口签名一字不变(D-46 精确落地)** —— `render/map/renderer.ts:234` 的自建构造调用改为 `this.autotile = manager.autotile;`,`AutotileProcessor` import 随之移除(该 import 已无使用点);`render/map/renderer.ts:71` 的 `readonly autotile: IAutotileProcessor` 逐字保留、`render/map/types.ts:334` 的 `IMapRenderer.autotile` 与 `render/map/types.ts:329` 的 `manager: ITextureManager` 一字不动。" + - "**改动边界未被扩大:范围外零改动、不新增 legacy 兼容相关内容、被删标识符零出现(D-47 / D-48 / D-49 / D-50 硬边界全部满足)** —— 不重新设计 `textures` / `tileStore` / `tiles` 的职责边界(地图背景继续走现有 `manager.getTile`,`ITextureGetter` 不变);`packages-user/client-base/src/material/` 下零改动;加载面(`client-modules/src/fallback/load.ts`、`client-base/src/load/**`)零改动;不添加任何 legacy 兼容相关任务、约束或验收项;`IBlockIdentifier` 在 `render/map/**` 内零出现。`packages/` 与 `src/` 零改动。" + - "**目标达成:三份范围内文件上 texture 归属 TS 诊断由 13 条归零**(vertex 6 + renderer 5 + hero 2 = 13),且不新增任何范围内诊断;`vue-tsc` 全仓错误总行数不因本计划上升(范围内 3 文件诊断数 = 0);`eslint`、`prettier --check`、CRLF 三文件全绿。" + - "**项目硬规则满足**:不修改任何既有 jsDoc / 注释文本;不新增注释中的低价值内容;不新增公共 / 受保护 / 私有成员或字段(Task 0 已呈报「无新命名」并获得批准);全部为普通 import(无 `import type`);import 仍集中在文件首部连续块、无注释 / 空行插入;不新增依赖、不动 `package.json` / `pnpm-lock.yaml` / `tsconfig.json`;`moving.ts` 与 `render/map/types.ts` 经核实无需改动(二者不含 `cls` 读写,仅承载类型面)。" + - "**只读基线以「状态零漂移 + 目标内容已变」方式断言,判据与提交状态无关**:Task 1 起始把 `git status --porcelain -- packages packages-user src` 与三份目标文件的内容 SHA-256 原样落盘到临时目录(`%TEMP%/opencode/04-09-baseline.txt` / `%TEMP%/opencode/04-09-baseline-hashes.txt`,均不在仓库内),Task 3 断言 ① 三份目标文件当前内容哈希均不同于基线(即确已被本计划改动,无论是否已被每任务提交协议提交),② 当前 `git status` 相对该基线除三份目标文件外无新增 / 消失 / 状态变化;用户对数据包的并发改动一律不被回滚 / 暂存 / 提交 / 修改。" + artifacts: + - path: packages-user/client-modules/src/render/map/vertex.ts + provides: "自动元件顶点生成器消费新接口:`updateAutotile` / `updateMoving` 改调 `IAutotileProcessor.render`;`checkAutotileConnectionAround` / `updateVertexArray` / `updateMoving` 的自动元件判定改走 `tileType === TileType.Autotile`;`BlockCls` import 移除、`TileType` 从 `@user/data-common` 引入" + - path: packages-user/client-modules/src/render/map/renderer.ts + provides: "`MapRenderer` 组合根:`readonly autotile: IAutotileProcessor` 字段保留并从 `manager.autotile` 赋值(D-46);`useTileBackground` 的背景自动元件分支改走 `render` + `tileType`;`AutotileProcessor` / `BlockCls` import 移除、`TileType` 引入" + - path: packages-user/client-modules/src/render/map/extension/hero.ts + provides: "勇士贴图 `IMaterialFramedData` 字面量以 `tileType: TileType.Unknown` 构造(D-45);`BlockCls` import 移除、`TileType` 并入既有 `@user/data-common` 导入块" + - path: .planning/phases/04-render-adaptation/04-09-SUMMARY.md + provides: "本增量的执行簿记:逐条 D-43..D-50 落点、三文件诊断 13→0 的门禁输出、并发基线保持证据、无新命名声明、REND-01 / REND-02 仍 Pending 的声明" + key_links: + - "`packages-user/client-base/src/material/types.ts:148-151`(`IAutotileProcessor.render(tile: IMaterialFramedData, connection: number): ITextureRenderable | null`)↔ 三处调用点 `packages-user/client-modules/src/render/map/vertex.ts:461`、`render/map/vertex.ts:874`、`render/map/renderer.ts:1253`(原 `renderWithoutCheck` 调用全部改 `render`,D-43)" + - "`packages-user/client-base/src/material/types.ts:79`(`IMaterialFramedData.tileType: TileType`)与 `packages-user/data-common/src/store/types.ts:6-25`(`TileType.Autotile`)↔ 五处判定 `render/map/renderer.ts:1252`、`render/map/renderer.ts:1263`、`render/map/vertex.ts:517`、`render/map/vertex.ts:561`、`render/map/vertex.ts:872` 与一处解构 `render/map/vertex.ts:852`(D-44)" + - "`packages-user/client-base/src/material/manager.ts:38` + `:90`(`TextureManager` 持有 `readonly autotile` 并在构造器内 `new AutotileProcessor(state)`)↔ `packages-user/client-modules/src/render/map/renderer.ts:71`(`readonly autotile: IAutotileProcessor` 保留)与 `:234`(改为 `manager.autotile`)↔ `packages-user/client-modules/src/render/map/types.ts:334`(`IMapRenderer.autotile` 签名不变)(D-46)" + - "`packages-user/client-modules/src/render/map/extension/hero.ts:164-170`(勇士贴图字面量)↔ `packages-user/client-modules/src/render/map/moving.ts:31` / `render/map/types.ts:204`(`IMaterialFramedData` 经 `IMovingBlock.texture` 流向 `vertex.ts:852` 的解构)(D-45 + 类型面)" + - "`git status --porcelain -- packages packages-user src` 状态基线 + 三份目标文件的内容 SHA-256 基线(Task 1 (0a)/(0b) 落盘,均在仓库外)↔ Task 3 的「状态零漂移 + 目标内容已变」断言(与每任务提交协议是否已提交无关)↔ 用户数据包改动零触碰证据" + - "`pnpm exec vue-tsc --noEmit` 的范围内三文件诊断数(Task 1 记录 13,Task 2 后应为 0)↔ Task 3 的范围内归零门禁 ↔ REND-01 texture 子切片完成证据(REND-01 / REND-02 仍 Pending)" + assumptions: + - "**FLAGGED ASSUMPTION(specless fallback)— REND-01 edge unresolved:** REND-01(渲染端适配新数据层接口)的整体验收边界未提供;本计划只完成其 texture 子切片,不发明整体阈值。" + - "**FLAGGED ASSUMPTION(specless fallback)— REND-02 edge unresolved:** REND-02(移动端与桌面端双布局)验收边界未提供;本计划不涉及双布局,也不发明其阈值。" + - "**无运行时渲染验证可得(诚实降级):** 仓库渲染端无运行时测试设施(04-07 已如实声明)。本计划的端到端可运行门禁为「范围内 TS 诊断归零 + eslint + prettier + CRLF + 符号零命中 + 基线增量」的静态组合,配合人工复核;**不得**以任何无法运行的运行时门禁冒充通过,也不得新增测试文件。" + - "**并发基线可能非空:** 用户持续手工改数据端(`data-base` / `data-common` / `data-system`),Task 1 起始的 `git status --porcelain -- packages packages-user src` 可能非空;本计划一律以当日实测为基线,**不回滚 / 不暂存 / 不提交 / 不修改**用户任何改动;断言只要求基线之外恰好新增三份目标文件。" + - "**行号以当日实测为准:** 本计划引用的 `file:line` 为规划日(2026-09-23,HEAD `c225fbe`,工作树干净)实测锚点;执行时若偏移,按符号名定位(`renderWithoutCheck`、`cls`、`BlockCls`、`AutotileProcessor`、`tileType`)并在 SUMMARY 记录实际行号。" + prohibitions: + - "不得在用户明确回复「可以执行」四个字之前执行本计划任何任务或派发任何执行子代理(AGENTS.md 第一个 block 点;不因触发方式或 `yolo` / `auto_advance` 放宽)。" + - "不得修改 `packages-user/client-base/src/material/` 下任何内容(D-42 / D-48);不得修改 `packages-user/client-base/src/types.ts`、`packages-user/client-modules/src/{client.ts,core.ts,types.ts}`。" + - "不得修改加载面:`packages-user/client-modules/src/fallback/load.ts`、`packages-user/client-base/src/load/**`(D-39 / D-41),其既有诊断(含 9 条 TS2554)一律只报告、不修复。" + - "不得修改 `packages/**`、`src/**`(D-33 / D-32 沿用);不得试图追平仓库既有无关类型错误。" + - "不得引入 legacy 兼容改动,也不得把「是否破坏 legacy 插件面」写成任务、约束或验收项(D-49;对应 04-08 台账 `#04-08-D-08` / `#04-08-F-07` 作废)。" + - "不得使用 `IBlockIdentifier`,也不得为其保留兼容分支(D-50);`render/map/**` 内该标识符必须零出现。" + - "不得新建 `BlockCls` → `TileType` 的映射表,也不用 `AutotileType` 做自动元件布局分类(D-44)。" + - "不得保留或新增「跳过检查」入口;三处调用点一律用 `render`(D-43)。" + - "不得新增 / 改名任何公共 / 受保护 / 私有成员、字段或方法名;不得新增依赖;不得修改 `package.json` / `pnpm-lock.yaml` / `tsconfig.json`。" + - "不得修改任何既有 jsDoc / 注释文本;不得新增 `// @ts-expect-error`,不得删除既有 `// @ts-expect-error`(`hero.ts` 的既有标注逐字保留)。" + - "不得改写 / 覆盖 / 改号 `04-01`..`04-08` 的既有 `*-PLAN.md` 与 `*-SUMMARY.md`。" + - "不得新增、删除或修改任何测试文件;不得把 `moving.ts` / `render/map/types.ts` 列入改动(经核实无需改动)。" + - "每条 `` 必须紧跟 ``;无法运行的运行时门禁必须显式声明不可得并降级,不得伪造绿。" +--- + + +Phase 4 第九个增量(用户裁定 2026-09-23 · D-43..D-50 **第二步·实施**)—— **TextureManager 新接口适应实施(代码改动)**。 + +**要解决的问题:** 用户以 `d36ea69 refactor: 贴图存储方式` 重写了贴图存储层,`04-TEXTURE-INTERFACE-IMPACT.md`(04-08 只读清点)确认地图渲染端仍有 **13 条** texture 归属 TS 诊断未跟上新接口(`render/map/vertex.ts` 6 条、`render/map/renderer.ts` 5 条、`render/map/extension/hero.ts` 2 条);同时台账 D 类 / F 类的未决项已由用户逐条裁定(D-43..D-50),本步即为按裁定落地的实施。 + +**大致解决方案:** 仅改三份地图渲染文件,按裁定做四类替换 —— ① `renderWithoutCheck` 的三处调用点改用新 `render(tile, connection)`(D-43);② `BlockCls` / `cls` 的判定与解构改用 `tileType === TileType.Autotile`(`TileType` 来自 `@user/data-common`,D-44);③ `hero.ts:167` 的勇士贴图字面量填 `tileType: TileType.Unknown`(D-45);④ `MapRenderer` 自建 `AutotileProcessor` 改为从 `manager.autotile` 取值(`AutotileProcessor` import 随之移除),`readonly autotile` 字段与 `IMapRenderer` 签名不变(D-46)。`moving.ts` / `render/map/types.ts` 经核实不含 `cls` 读写、无需改动;`material/`、加载面、`packages/`、`src/` 一律零改动(D-39 / D-41 / D-42 / D-48)。 + +**逐条裁定编码(本步的验收锚点):** + +| 裁定 | 落点 | 要求 | +|---|---|---| +| D-43 | `render/map/vertex.ts:461`、`render/map/vertex.ts:874`、`render/map/renderer.ts:1253` | 改调 `render(tile, connection)`;不得保留 / 新增「跳过检查」入口 | +| D-44 | `render/map/renderer.ts:1252`、`:1263`、`render/map/vertex.ts:517`、`:561`、`:872`(判定)与 `render/map/vertex.ts:852`(解构键) | `tileType === TileType.Autotile`;不做映射表;`AutotileType` 不参与分类 | +| D-45 | `render/map/extension/hero.ts:167` | `tileType: TileType.Unknown`;该文件在范围内 | +| D-46 | `render/map/renderer.ts:71`(保留字段)、`:234`(改 `manager.autotile`)、`render/map/types.ts:334`(签名不动) | 不再自建处理器 | +| D-47 | 地图背景 `manager.getTile` 路径 | 只做接口适应,不重设计 `textures` / `tileStore` / `tiles` | +| D-48 | `packages-user/client-base/src/material/**` | AI 零改动(写入侧归用户加载系统) | +| D-49 | 全计划 | 不考虑 legacy 兼容,不设相关任务 / 约束 / 验收项 | +| D-50 | `render/map/**` | `IBlockIdentifier` 零出现;其消费点属加载面、归用户 | + +**目标:** 三份文件上 13 条 texture 归属诊断归零(`vue-tsc` 范围内 3 文件诊断数 = 0),`eslint` / `prettier --check` / CRLF 全绿;`REND-01` / `REND-02` 保持 **Pending**(本步仅为 REND-01 的 texture 子切片)。 + +**第一步是强制汇报关卡(AGENTS.md):** 本计划以 `checkpoint:decision` + `gate="blocking-human"` 的 Task 0 开头,`autonomous: false`;未收到用户「可以执行」四个字前不得执行任何步骤。 + +## Source Audit(多源覆盖审计) + +| SOURCE | ID | Feature/Requirement | Plan | Status | Notes | +|--------|----|---------------------|------|--------|-------| +| GOAL | — | Phase 4:「渲染端通过新数据层接口驱动,并同时支持移动端与桌面端布局」 | 04-09 | COVERED(增量·texture 实施子切片) | 只闭合地图渲染的 texture 接口适配;双布局属后续增量(见 assumptions) | +| REQ | REND-01 | 渲染端适配新数据层接口 | 04-09 | COVERED(子切片;需求保持 Pending) | texture / 素材接口是渲染端素材公共面;本步只做地图渲染消费者适配;整体验收边界未提供 → FLAGGED ASSUMPTION | +| REQ | REND-02 | 渲染端同时支持移动端与桌面端布局 | 04-09 | COVERED(不涉及) | 本步不含双布局;验收边界未提供 → FLAGGED ASSUMPTION | +| RESEARCH | — | (本阶段无 `04-RESEARCH.md`) | — | N/A | 等价输入 = `04-TEXTURE-INTERFACE-IMPACT.md`(A13/B8/C8/D9/E7/F8)与 `d36ea69` 接口基准 | +| CONTEXT | D-43 | `renderWithoutCheck` 的替代 = 直接用新 `render(tile, connection)`,3 处调用点全改 | 04-09 | COVERED | Task 1(1 处)+ Task 2(2 处),逐处双锚点门禁 | +| CONTEXT | D-44 | `BlockCls` 的替代 = `TileType`;判定走 `tileType === TileType.Autotile`;不做映射表 | 04-09 | COVERED | Task 1(动态路径 1 解构 + 1 判定)+ Task 2(4 处判定 + import 清理) | +| CONTEXT | D-45 | `extension/hero.ts:167` 用 `tileType: TileType.Unknown`;D-18 不排除该文件 | 04-09 | COVERED | Task 1 | +| CONTEXT | D-46 | `MapRenderer` 不再自建 `AutotileProcessor`;保留字段签名,构造器取 `manager.autotile` | 04-09 | COVERED | Task 1 | +| CONTEXT | D-47 | `textures` / `tileStore` / `tiles` 职责边界不在本步;背景继续 `manager.getTile` | 04-09 | COVERED(边界) | 无任务改动背景取值路径 | +| CONTEXT | D-48 | `material/` 零改动,写入侧归用户 | 04-09 | COVERED(禁止) | prohibitions + forbidden-path 门禁 | +| CONTEXT | D-49 | 不考虑 legacy 兼容 | 04-09 | COVERED(禁止) | prohibitions 明列;无相关任务 / 约束 / 验收项 | +| CONTEXT | D-50 | `IBlockIdentifier` 用法删除;本步范围内不得出现 | 04-09 | COVERED | 符号零命中门禁含该标识符;消费点 `fallback/load.ts` 属加载面不纳入 | +| CONTEXT | D-33 | 范围外一律不改;不追求解决全部类型错误 | 04-09 | COVERED(边界) | forbidden-path 门禁 + 只断言范围内 3 文件 | +| CONTEXT | D-23 | 不使用 `client` / `state` 全局单例(仅约束新增 / 后续) | 04-09 | COVERED | 本步零新增代码;`state` 注入不属本步;不因存量单例阻塞 | +| CONTEXT | D-01..D-22, D-24..D-42 | 前序决策与已执行增量 | 04-01..04-08 | COVERED(前序计划) | `04-TEXTURE-INTERFACE-IMPACT.md` 是本步的权威输入 | +| CONTEXT | Deferred Ideas | 阶段 4 后续步骤(双布局实现);`packages` 全部、`entry-client`、`legacy-plugin-*`、`data-fallback` | — | DEFERRED | 未写入任何任务 | +| AUDIT | #04-08-C-01/C-02/C-03 | 地图渲染三文件定位索引(texture 归属 13 条) | 04-09 | COVERED | Task 1/2 逐处落地;C-04/C-05 经核实无需改动 | + +## Estimate Basis(估算依据) + +`estimate-calibration` 无历史样本(`factor: 1`、`confidence: low`),故 `tokens == raw_tokens` 并如实标注 `confidence: low`。按执行者实际触及量估算:Task 0 只读汇报并等待确认(约 4k);Task 1(tracer)三文件共 8 处编辑 + 首次 `vue-tsc` 全量(约 1–3 分钟)+ 基线落盘(约 20k);Task 2 两文件剩余 8 处编辑与 import 清理(约 15k);Task 3 六道门禁重跑 + 人工复核 + `04-09-SUMMARY.md` 落盘提交(约 10k);固定开销约 3k;合计约 52k。 + + + +@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-TEXTURE-INTERFACE-IMPACT.md +@.planning/phases/04-render-adaptation/04-08-SUMMARY.md +@.planning/phases/04-render-adaptation/04-07-PLAN.md +@AGENTS.md +@dev.md + + +## Artifacts this phase produces + +| Artifact | Path | Purpose | +|---|---|---| +| 自动元件顶点生成器(改) | `packages-user/client-modules/src/render/map/vertex.ts` | 消费 `IAutotileProcessor.render` 与 `TileType.Autotile`;移除 `BlockCls` import,引入 `TileType` | +| 地图渲染器组合根(改) | `packages-user/client-modules/src/render/map/renderer.ts` | `autotile` 取自 `manager.autotile`(D-46);背景自动元件分支改 `render` + `tileType`;import 清理 | +| 勇士渲染扩展(改) | `packages-user/client-modules/src/render/map/extension/hero.ts` | 贴图字面量填 `tileType: TileType.Unknown`(D-45);import 清理 | +| 执行簿记(新) | `.planning/phases/04-render-adaptation/04-09-SUMMARY.md` | 逐条裁定落点、13→0 门禁输出、基线保持证据、无新命名声明 | +| 路线图登记(改) | `.planning/ROADMAP.md` | Phase 4 Wave 8 的 `04-09-PLAN.md` 条目与登记说明 | +| 本计划(新) | `.planning/phases/04-render-adaptation/04-09-PLAN.md` | 本文件 | + + + + + + + + + Task 0: 预执行汇报关卡 —— 汇报 TextureManager 新接口适应实施(D-43..D-50)的对象、逐条裁定编码、四类替换方案与命名审批结论,等待用户回复「可以执行」 + 用户裁定 D-43..D-50 已写入 `.planning/phases/04-render-adaptation/04-CONTEXT.md`(以文件内实际存在该区间为准);`d36ea69` 是当日 HEAD 的祖先(`git merge-base --is-ancestor d36ea69 HEAD` 退出码 0);`04-TEXTURE-INTERFACE-IMPACT.md` 存在且含 A/B/C/D/E/F 六类台账 + (只读汇报,不修改任何文件) + + - AGENTS.md(第一个 block 点必须汇报并等待;只有「可以执行」四个字后才允许执行;命名必须先反馈;发现问题须经确认) + - .planning/phases/04-render-adaptation/04-CONTEXT.md(D-43..D-50 裁定原文;D-33 / D-42 / D-23) + - .planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md(A13/B8/C8/D9/E7/F8;D 类 3 处调用点与 `material/utils.ts` 用户自有面;E 类加载面;F 类未决项) + - .planning/phases/04-render-adaptation/04-09-PLAN.md(本计划全文:objective 的裁定编码表、tasks、禁令、门禁) + - packages-user/client-base/src/material/types.ts:75-86,148-151(`IMaterialFramedData.tileType`;`IAutotileProcessor.render`) + - packages-user/client-base/src/material/manager.ts:37-94(`TextureManager.autotile` 与构造器赋值) + - packages-user/data-common/src/store/types.ts:6-25(`TileType.Autotile`) + - packages-user/client-modules/src/render/map/renderer.ts:8-15,71,234,1243-1271(待改点) + - packages-user/client-modules/src/render/map/vertex.ts:24-29,446-479,510-528,537-582,846-913(待改点) + - packages-user/client-modules/src/render/map/extension/hero.ts:1-19,144-173(待改点) + - dev.md(「无类型导入」「引入位置」「命名规则」「换行使用 CRLF」) + + + **停下并向用户汇报,等待明确回复后才继续;汇报本身只读,不写任何文件、不改任何代码、不派发子代理。** + + ① **要解决的问题**:`d36ea69` 重写贴图存储层后,地图渲染端 13 条 texture 归属 TS 诊断未跟上新接口(`render/map/vertex.ts` 6 条、`render/map/renderer.ts` 5 条、`render/map/extension/hero.ts` 2 条)。04-08 只读台账的 D 类 / F 类未决项已由用户逐条裁定为 D-43..D-50,本步按裁定落地。 + + ② **逐条裁定编码(须原样汇报 objective 的编码表)**:D-43(三处调用点改 `render(tile, connection)`,不保留跳过检查入口);D-44(五处判定与一处解构改 `tileType === TileType.Autotile`,不做映射表、不用 `AutotileType` 分类);D-45(`extension/hero.ts:167` 填 `tileType: TileType.Unknown`,该文件在范围内);D-46(`MapRenderer` 保留 `readonly autotile` 字段与 `IMapRenderer` 签名,构造器改取 `manager.autotile`,不再自建处理器);D-47(不重设计 `textures` / `tileStore` / `tiles`,背景继续 `manager.getTile`);D-48(`material/` 零改动);D-49(不考虑 legacy 兼容,相关顾虑作废);D-50(`IBlockIdentifier` 零出现,其消费点属加载面归用户)。 + + ③ **大致解决方案与对象**:只改 `packages-user/client-modules/src/render/map/vertex.ts`、`render/map/renderer.ts`、`render/map/extension/hero.ts` 三份文件,做四类替换(方法名 `render`、判定 / 解构 `tileType`、勇士字面量 `tileType: TileType.Unknown`、`manager.autotile` 取值),并清理随之失去使用点的 `BlockCls` / `AutotileProcessor` import、在需要处引入 `TileType`(`@user/data-common`)。**`renderer.ts` 的 `AutotileProcessor` import 必须与 `:234` 的替换在同一步内完成**(替换后该 import 再无使用点,而 eslint 的 `@typescript-eslint/no-unused-vars` 为 `error`,留着必然使该步 eslint 门禁失败);`BlockCls` import 则在其各自文件最后一次使用被替换掉时同步移除。经核实 `render/map/moving.ts` 与 `render/map/types.ts` 不含 `cls` 读写,**无需改动**,不列入改动清单。 + + ④ **明确不做的事(须原样告知)**:不改 `material/**`、不改加载面(`fallback/load.ts`、`client-base/src/load/**`)、不改 `packages/**` 与 `src/**`、不追平仓库既有无关类型错误、不加 legacy 兼容、不加 / 删 `// @ts-expect-error`、不改任何既有 jsDoc / 注释、不新增依赖、不动 `package.json` / `pnpm-lock.yaml` / `tsconfig.json`、不新增或修改测试、不新增 / 改名任何成员或字段;**范围外已知余留(只报告、不修改)**:`packages-user/client-modules/src/client.ts:14` 的 `IAutotileProcessor` 与 `:16` 的 `AutotileProcessor` 是 `d36ea69` 删除 `IClientBase.autotile` 后遗留的孤立未使用 import,属 D-33 范围外,本计划不改动,仅在 `04-09-SUMMARY.md` 如实记录。 + + ⑤ **命名审批(AGENTS.md 要求,须明确呈报并请求批准)**:本计划**不新增、不改名任何公共 / 受保护 / 私有成员、字段或方法名**;唯一的标识符变化是 `vertex.ts` 内一处**局部对象解构绑定**由 `cls` 改为 `tileType`(绑定名取自既有接口成员 `IMaterialFramedData.tileType`,非新命名),以及引入既有枚举 `TileType`。请用户确认该结论;若用户要求任何命名调整,按 ⑧ 退出修订。 + + ⑥ **验证方式(须原样告知)**:`pnpm exec vue-tsc --noEmit`(断言三份范围内文件诊断数 = 0,规划日 13 条);`pnpm exec eslint <三文件>`;`pnpm exec prettier --check <三文件>`;CRLF 三文件门禁;`git grep` 符号零命中(`render/map/**` 内 `BlockCls`、`renderWithoutCheck`、`IBlockIdentifier` 均 0);基线门禁 = 状态零漂移 + 三份目标文件内容哈希已变(**与每任务提交协议是否已提交无关**)+ 禁用路径零改动;人工复核。**渲染端无运行时测试设施,运行时 UAT 不可得**,如实声明,不以无法运行的门禁冒充通过。 + + ⑦ **编号与 wave(须原样告知)**:本计划为 **04-09**(第九个增量),`wave: 8`,`depends_on` 列 `04-01`..`04-08`;phase 目录内既有 `04-01`..`04-08` 的 `*-PLAN.md` 与 `*-SUMMARY.md` 一律不得改写 / 覆盖 / 改号。 + + ⑧ **关卡性质与失败语义(须原样告知)**:无论触发方式或配置模式(`mode: yolo`、`workflow.auto_advance`)如何,执行者 / 子代理都必须在此停下并等待用户明确回复「可以执行」四个字;未收到即继续、或回复其它内容(含修订意见、沉默、自动模式推进、子代理自行放行),一律视为未通过本关卡,必须立即停止、保持只读、不写任何文件。若用户给出修订意见(不同意替换范围 / 命名结论 / 裁定编码),退出本计划、修订 `04-09-PLAN.md` 后重新汇报,不得自行改道执行。 + + 是否按本计划开始执行 TextureManager 新接口适应实施(对象 = `render/map/{vertex,renderer,extension/hero}.ts`;按 D-43..D-50 做四类替换;`material/`、加载面、`packages/`、`src/` 零改动;不新增 / 改命名;`autonomous: false`) + AGENTS.md 要求任何计划的第一个 block 点必须是向用户汇报并等待确认;本次涉及接口适应与局部绑定调整,须先确认命名结论无新命名。`.planning/config.json` 的 `mode: yolo` 会使执行默认自动推进,故必须显式置 `blocking-human` 且 `autonomous: false`。 + + + + + 回复「可以执行」四个字(修订意见请按 action ⑧ 处理) + + 用户在对话中明确回复「可以执行」四个字(人工核对回复原文;本关卡为人工关卡,失败语义写在 Task 0 的 action ⑧ 与 acceptance_criteria 中) + + + - 用户回复中明确包含「可以执行」四个字 + - 汇报涵盖:要解决的问题(13 条范围内诊断)、大致解决方案(三文件四类替换)、逐条裁定编码(D-43..D-50)、明确不做的事、验证方式、编号与 wave + - 汇报中已呈报命名审批结论(本计划无新命名;仅一处局部解构绑定 `cls`→`tileType` 与既有枚举 `TileType` 引入)并请求确认 + - 汇报中已声明范围外已知余留(`packages-user/client-modules/src/client.ts:14` / `:16` 的孤立未使用 import)只报告、不处置 + - 汇报中已声明运行时 UAT 不可得(渲染端无运行时测试设施) + - 汇报中已声明关卡性质:不因触发方式或 `yolo` / `auto_advance` 自动放行 + + 用户明确回复「可以执行」;在此之前未执行任何任务、未修改任何文件、未派发任何执行子代理 + + + + Task 1(tracer):一条端到端路径 —— 勇士贴图字面量(`tileType: TileType.Unknown`)→ 移动图块 `IMaterialFramedData` → 顶点动态分支(`tileType` 判定 + `render`)→ `MapRenderer.autotile` 取自 `manager.autotile` + Task 0 已闭环:用户在对话中明确回复「可以执行」四个字(未收到则不得开始本任务) + packages-user/client-modules/src/render/map/extension/hero.ts, packages-user/client-modules/src/render/map/vertex.ts, packages-user/client-modules/src/render/map/renderer.ts + + - packages-user/client-modules/src/render/map/extension/hero.ts:1-19,144-173(`@user/data-common` 导入块、`@user/client-base` 的 `BlockCls` / `IMaterialFramedData` 导入、`updateHeroTexture` 的对象字面量) + - packages-user/client-modules/src/render/map/vertex.ts:24-29,846-913(`BlockCls` / `IMaterialFramedData` 导入;`updateMoving` 的解构与自动元件分支) + - packages-user/client-modules/src/render/map/renderer.ts:8-15,217-240(`@user/client-base` 导入块;构造器与 `autotile` 赋值点) + - packages-user/client-base/src/material/types.ts:75-86,148-151(`IMaterialFramedData` 现行成员;`render(tile, connection)` 签名) + - packages-user/client-base/src/material/manager.ts:37-40,84-94(`TextureManager.autotile` 成员与构造器赋值) + - packages-user/data-common/src/store/types.ts:6-25(`TileType.Autotile` / `TileType.Unknown`) + - packages-user/client-modules/src/render/map/moving.ts:26-107(`IMovingBlock.texture` 与本路径的类型面;本任务不改该文件) + - packages-user/client-modules/src/render/map/types.ts:195-230,320-345,525-545(`IMovingBlock.texture` / `IMapRenderer.manager` / `IMapRenderer.autotile` / `addMovingBlock` 签名;本任务不改该文件) + - dev.md(「无类型导入」「引入位置」「命名规则」「对象解构」;「换行使用 CRLF」) + + + (0a) **只读起始基线(先做)**:执行 `git status --porcelain -- packages packages-user src`,把输出**原样逐行**写入临时文件 `%TEMP%/opencode/04-09-baseline.txt`(不存在则创建该目录;文件内容保留原始 BOM 与行序;输出为空则写入零字节并在汇报中记「(无)」)。**不得回滚 / 暂存 / 提交 / 修改**任何既有改动;该文件位于仓库外,不进入 `git status`。同时执行 `pnpm exec vue-tsc --noEmit` 并把完整输出存到 `%TEMP%/opencode/04-09-tsc-before.txt`,记录**范围内三文件**的诊断行数与逐行内容(规划日实测 = 13 条:`vertex.ts` 6 / `renderer.ts` 5 / `hero.ts` 2),供 Task 3 对比。 + + (0b) **三份目标文件的内容哈希基线(与 (0a) 同为改动前、必须先做)**:用 Node 的 `crypto` 计算三份目标文件**改动前**内容的 SHA-256,写入 `%TEMP%/opencode/04-09-baseline-hashes.txt`,每行格式 ` <相对路径>`(两空格分隔,路径用正斜杠,行序 = `vertex.ts` / `renderer.ts` / `hero.ts`),例如 `crypto.createHash('sha256').update(fs.readFileSync(f)).digest('hex')`。**原因(必须遵守)**:执行器按每任务提交协议会在 Task 1 / Task 2 结束时分别提交这三份文件,Task 3 不得再以「`git status` 是否仍显示它们为已修改」判断目标是否被改动;内容哈希是与提交状态无关的判据。该文件同样位于仓库外,不得回滚 / 暂存 / 提交 / 修改任何既有改动。 + + (1) **`extension/hero.ts`(D-45)**:把 `TileType` 并入既有的 `@user/data-common` 导入块(该块当前以 `ObjectMoveType` 结尾,`TileType` 追加为该块最后一项,保持 import 连续块、不插入注释或空行);从 `@user/client-base` 的导入中仅移除 `BlockCls`、保留 `IMaterialFramedData`;把 `:167` 的对象字面量成员 `cls: BlockCls.Unknown,` 改为 `tileType: TileType.Unknown,`(其余 `offset` / `texture` / `frames` / `defaultFrame` 逐字保留;不改动该文件既有的 `// @ts-expect-error` 标注与 `state` 存量用法)。 + + (2) **`vertex.ts` 动态路径(D-43 / D-44)**:在导入块中加入 `import { TileType } from '@user/data-common';`(置于既有 `@user/data-base` 导入之后、连续块内、无空行);在 `updateMoving` 中把对象解构的键 `cls` 改为 `tileType`(保持多键解构形态);把紧随其后的自动元件判定由读取 `cls` 比较改为 `tileType === TileType.Autotile`;把该分支内的自动元件渲染调用由旧的「跳过检查」方法改为 `this.renderer.autotile.render(block.texture, 0b0000_0000)`(连接常量与后续 `ITextureRenderable` 判空逻辑保持不变)。**本任务暂不移除 `vertex.ts` 的 `BlockCls` 导入**(`:517` / `:561` 仍在使用,同 Task 2 一并清理)。 + + (3) **`renderer.ts` 组合根(D-46)**:把构造器内自建自动元件处理器的赋值(`:234`)改为 `this.autotile = manager.autotile;`;**并在同一编辑内从 `@user/client-base` 导入块移除 `AutotileProcessor`** —— 该 import 在 `:234` 被替换后已无任何使用点,而 `eslint.config.js` 的 `@typescript-eslint/no-unused-vars` 为 `error`,若留着会使本任务的 eslint 门禁必然失败(本任务不得留下任何未使用 import)。`readonly autotile: IAutotileProcessor;` 字段声明(`:71`)与 `render/map/types.ts` 的 `IMapRenderer.autotile`(`:334`)**一字不动**(`IAutotileProcessor` 类型 import 仍被 `:71` 使用,必须保留);`BlockCls` 导入本任务**保留**(`:1252` / `:1263` 仍在使用,Task 2 随判定替换一并移除);构造器其余语句、`manager` 参数的 `readonly` 形态、`vertex` / `viewport` / `tileAnimater` 初始化顺序保持不变。 + + (4) 本任务只改上述三份文件;不得改 `moving.ts` / `render/map/types.ts`、不得改 `material/**`、不得改加载面、不得新增测试;不得修改任何既有注释文本。 + + 三处改动都是把消费者指向新接口的机械替换(方法名、成员名、取值来源),可用一次 git 操作整体回退;不引入新契约、不改公共签名。 + + node -e "const fs=require('fs'),path=require('path'),{execSync}=require('child_process');let o='';try{o=execSync('pnpm exec vue-tsc --noEmit',{encoding:'utf8',maxBuffer:1<<28,stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'')}const scoped=['packages-user/client-modules/src/render/map/vertex.ts','packages-user/client-modules/src/render/map/renderer.ts','packages-user/client-modules/src/render/map/extension/hero.ts'];const cnt=t=>t.split(/\r?\n/).map(l=>l.replace(/\\/g,'/')).filter(l=>/error TS\d+/.test(l)&&scoped.some(f=>l.indexOf(f)>=0)).length;const bp=path.join(process.env.TEMP||process.env.TMP,'opencode','04-09-tsc-before.txt');if(!fs.existsSync(bp)){console.error('MISSING TSC BEFORE FILE');process.exit(1)}const before=cnt(fs.readFileSync(bp,'utf8')),after=cnt(o);if(before-after!==5){console.error('TRACER DELTA WRONG before='+before+' after='+after);process.exit(1)}console.log('OK tracer delta -5 (before '+before+' after '+after+')')" + non-zero exit, 或 stderr 含 "MISSING TSC BEFORE FILE" / "TRACER DELTA WRONG"(未落盘 (0a) 的类型基线,或 tracer 未恰好消解范围内 5 条诊断 —— 改动缺失或改错) + node -e "const{execSync}=require('child_process');const files=['packages-user/client-modules/src/render/map/vertex.ts','packages-user/client-modules/src/render/map/renderer.ts','packages-user/client-modules/src/render/map/extension/hero.ts'];try{execSync('pnpm exec eslint '+files.join(' '),{encoding:'utf8',stdio:['ignore','pipe','pipe']});console.log('OK eslint')}catch(e){console.error('ESLINT FAILED :: '+(e.stdout||'')+(e.stderr||''));process.exit(1)}" + non-zero exit, 或 stderr 含 "ESLINT FAILED"(三份文件 eslint 报错,通常是未使用 import 或导入顺序问题) + node -e "const fs=require('fs'),path=require('path');const dir=path.join(process.env.TEMP||process.env.TMP,'opencode');const bp=path.join(dir,'04-09-baseline.txt'),hp=path.join(dir,'04-09-baseline-hashes.txt');if(!fs.existsSync(bp)||!fs.existsSync(hp)){console.error('MISSING BASELINE FILE');process.exit(1)}const target=['packages-user/client-modules/src/render/map/vertex.ts','packages-user/client-modules/src/render/map/renderer.ts','packages-user/client-modules/src/render/map/extension/hero.ts'];const lines=fs.readFileSync(hp,'utf8').split(/\r?\n/).map(l=>l.trim()).filter(Boolean);const miss=target.filter(t=>!lines.some(l=>l.replace(/\\/g,'/').endsWith(t)));if(miss.length){console.error('BASELINE HASH MISSING '+miss.join(' ; '));process.exit(1)}const s=fs.readFileSync('.planning/phases/04-render-adaptation/04-09-PLAN.md','utf8');if(s.indexOf('tileType: TileType.Unknown')<0){console.error('D-45 LITERAL MISSING IN PLAN');process.exit(1)}console.log('OK baseline + hash baseline present ('+lines.length+' targets) + D-45 literal declared')" + non-zero exit, 或 stderr 含 "MISSING BASELINE FILE" / "BASELINE HASH MISSING" / "D-45 LITERAL MISSING IN PLAN"(未落盘 (0a) 的状态基线或 (0b) 的三份目标文件内容哈希基线,Task 3 无法断言增量) + + + - `hero.ts`:`BlockCls` 不再出现在该文件;`TileType` 位于既有 `@user/data-common` 导入块内;`:167` 字面量为 `tileType: TileType.Unknown`;既有 `// @ts-expect-error` 与 `state` 用法逐字保留 + - `vertex.ts`:`updateMoving` 解构键为 `tileType`,判定为 `tileType === TileType.Autotile`,调用为 `this.renderer.autotile.render(block.texture, 0b0000_0000)`;已加入 `@user/data-common` 的 `TileType` 导入;`BlockCls` 导入本任务保留待 Task 2 清理 + - `renderer.ts`:构造器为 `this.autotile = manager.autotile;`;`AutotileProcessor` 已从 `@user/client-base` 导入移除(`:234` 替换后已无使用点,否则 eslint `no-unused-vars` 必然报错);`BlockCls` 导入本任务保留待 Task 2 清理;`readonly autotile: IAutotileProcessor;` 与 `IMapRenderer.autotile` 签名未改 + - `moving.ts`、`render/map/types.ts`、`material/**`、加载面、`packages/**`、`src/**` 零改动;无新增测试;无既有注释改动;无新增依赖 + - 只读起始基线已落盘至仓库外(`%TEMP%/opencode/04-09-baseline.txt` 与 `%TEMP%/opencode/04-09-baseline-hashes.txt`),且未触碰用户任何改动 + + tracer 路径闭环:勇士贴图 → 移动图块 → 顶点动态分支 → `manager.autotile` 全部走新接口;该路径 **5** 个诊断行转绿(`hero.ts:15` / `hero.ts:167` / `vertex.ts:852` / `vertex.ts:874` / `renderer.ts:234`;`vertex.ts:872` 的 `cls` 来自 `:852` 的解构,TS 只在 `:852` 报错,故本任务消解 5 条而非 6 条);eslint 三文件通过;状态基线与三份目标文件内容哈希基线均已落盘 + + + + Task 2:补齐剩余转换 —— `vertex.ts` 静态判定与 `updateAutotile` 调用、`renderer.ts` 背景自动元件分支与 import 清理 + packages-user/client-modules/src/render/map/vertex.ts, packages-user/client-modules/src/render/map/renderer.ts + Task 1 已闭环:tracer 路径的 5 个诊断行转绿、状态基线与内容哈希基线均已落盘 + + - packages-user/client-modules/src/render/map/vertex.ts:24-29,440-479,510-582(`BlockCls` / `IMaterialFramedData` 导入;`updateAutotile` 的自动元件渲染调用;`checkAutotileConnectionAround` 与 `updateVertexArray` 的判定) + - packages-user/client-modules/src/render/map/renderer.ts:8-15,1231-1271(`@user/client-base` 导入块;`useTileBackground` 的单帧 / 多帧分支) + - packages-user/client-base/src/material/types.ts:148-162(`render` 与 `renderAnimated` 现行签名;`renderAnimated` 是已适配点、本任务不得改动) + - packages-user/data-common/src/store/types.ts:6-25(`TileType.Autotile`) + - dev.md(「无类型导入」「引入位置」「命名规则」「换行使用 CRLF」) + + + (1) **`vertex.ts` 静态路径(D-43 / D-44 / import 清理)**:把 `updateAutotile` 内的自动元件渲染调用由「跳过检查」方法改为 `autotile.render(tile, connection)`(传入参数与返回值处理不变,`const renderable = ...` 的判空与后续 `updateTileVertex` 调用逐字保留);把 `checkAutotileConnectionAround` 的判定改为 `if (!tile || tile.tileType !== TileType.Autotile) return;`;把 `updateVertexArray` 的自动元件判定改为 `if (tile.tileType === TileType.Autotile) {`;最后从 `@user/client-base` 导入中移除 `BlockCls`(仅保留 `IMaterialFramedData`)。`:852` / `:872` 已于 Task 1 处理,本任务不得回改。`render/map/renderer.ts:1264` 的 `renderAnimated` 调用是已适配点,**不得改动**。 + + (2) **`renderer.ts` 背景分支与 import 清理(D-43 / D-44)**:把 `useTileBackground` 单帧分支的判定改为 `if (tex.tileType === TileType.Autotile) {`,其内调用改为 `this.autotile.render(tex, 0b1111_1111)!`(保留末尾非空断言与返回值直返形态);把多帧分支的判定改为 `if (tex.tileType === TileType.Autotile) {`(其后 `this.autotile.renderAnimated(tex, 0b1111_1111)` 保持不变);在导入块中加入 `import { TileType } from '@user/data-common';`(置于既有 `@user/data-base` 导入块之后、连续块内);从 `@user/client-base` 导入中移除 `BlockCls`(`AutotileProcessor` 已于 Task 1 随 `:234` 替换一并移除,本任务不得回加;保留 `IAutotileProcessor` / `IMaterialFramedData` / `ITextureManager` / `ITrackedAssetData` —— 四者仍有使用点)。 + + (3) **完成后自检**:确认 `render/map/**` 内已无 `cls` 成员读写、无「跳过检查」调用、无 `BlockCls` 记号、无 `IBlockIdentifier`;确认 `moving.ts` / `render/map/types.ts` 未被修改;确认 CRLF 未被破坏。 + + (4) 本任务只改 `vertex.ts` 与 `renderer.ts`;不得改 `hero.ts` 既有结论、不得改 `material/**`、不得改加载面、不得新增测试、不得修改既有注释文本。 + + 与 Task 1 同类机械替换,整体可回退;不改任何公共签名或存储设计(D-47)。 + + + node -e "const{execSync}=require('child_process');let o='';try{o=execSync('pnpm exec vue-tsc --noEmit',{encoding:'utf8',maxBuffer:1<<28,stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'')}const scoped=['packages-user/client-modules/src/render/map/vertex.ts','packages-user/client-modules/src/render/map/renderer.ts','packages-user/client-modules/src/render/map/extension/hero.ts'];const hits=o.split(/\r?\n/).map(l=>l.replace(/\\/g,'/')).filter(l=>/error TS\d+/.test(l)&&scoped.some(f=>l.indexOf(f)>=0));if(hits.length){console.error('SCOPED TS ERRORS '+hits.length+' :: '+hits.slice(0,8).join(' @ '));process.exit(1)}console.log('OK zero scoped TS errors ('+scoped.length+' files)')" + non-zero exit, 或 stderr 含 "SCOPED TS ERRORS"(三份范围内文件仍有 TS 诊断,13→0 未达成) + node -e "const fs=require('fs');const files=['packages-user/client-modules/src/render/map/vertex.ts','packages-user/client-modules/src/render/map/renderer.ts','packages-user/client-modules/src/render/map/extension/hero.ts'];const bad=files.filter(f=>{const s=fs.readFileSync(f,'utf8');const lf=(s.match(/\n/g)||[]).length,crlf=(s.match(/\r\n/g)||[]).length;return lf!==crlf});if(bad.length){console.error('NON-CRLF '+bad.join(' ; '));process.exit(1)}console.log('OK CRLF 3 files')" + non-zero exit, 或 stderr 含 "NON-CRLF"(三份文件出现非 CRLF 换行,违反 dev.md「换行使用 CRLF 格式」) + node -e "const{execSync}=require('child_process');const names=['BlockCls','renderWithoutCheck','IBlockIdentifier'];const bad=[];for(const n of names){let o='',st=0;try{o=execSync('git grep -n -- '+n+' -- packages-user/client-modules/src/render',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'');st=e.status}const lines=o.replace(/\s+$/,'').length?o.replace(/\s+$/,'').split(/\r?\n/):[];if(lines.length)bad.push(n+' => '+lines.slice(0,3).join(' ; '));else if(st!==0&&st!==1)bad.push(n+' grep status '+st)}if(bad.length){console.error('SCOPE SYMBOL STILL PRESENT: '+bad.join(' || '));process.exit(1)}console.log('OK zero scoped symbols ('+names.length+')')" + non-zero exit, 或 stderr 含 "SCOPE SYMBOL STILL PRESENT"(`render/map/**` 内仍命中旧符号 / 被删标识符,替换未彻底) + node -e "const fs=require('fs');const t=fs.readFileSync('packages-user/client-modules/src/render/map/vertex.ts','utf8')+fs.readFileSync('packages-user/client-modules/src/render/map/renderer.ts','utf8');const bad=[];if(/\bcls\b/.test(t.replace(/\/\/[^\n]*/g,'')))bad.push('cls residual');if(t.indexOf('tileType === TileType.Autotile')<0)bad.push('no tileType comparison');if(t.indexOf(\"from '@user/data-common'\")<0)bad.push('TileType import missing');if(bad.length){console.error('CONVERSION INCOMPLETE '+bad.join(' | '));process.exit(1)}console.log('OK conversion markers present')" + non-zero exit, 或 stderr 含 "CONVERSION INCOMPLETE"(`cls` 成员读写残留,或缺少 `tileType` 判定,或未引入 `TileType`) + + + - `vertex.ts`:三处旧的「跳过检查」/ `BlockCls` 使用全部转为 `render` / `tileType`;`@user/client-base` 导入仅剩 `IMaterialFramedData`;`TileType` 已引入 + - `renderer.ts`:`useTileBackground` 两处判定为 `tex.tileType === TileType.Autotile`,单帧分支调用为 `this.autotile.render(tex, 0b1111_1111)!`,多帧分支 `renderAnimated` 未改;`BlockCls` 已从导入移除(`AutotileProcessor` 已于 Task 1 移除,未回加);`TileType` 已引入 + - `render/map/**` 内 `BlockCls` / 旧的「跳过检查」调用 / `IBlockIdentifier` 全部零命中;`cls` 成员读写零残留 + - 三份范围内文件 `vue-tsc` 诊断数 = 0;CRLF 保持;`moving.ts` / `render/map/types.ts` 零改动 + + 剩余转换完成,范围内三文件 texture 归属诊断由 13 归零;符号零命中与 CRLF 门禁转绿;未触碰 `material/`、加载面与范围外文件 + + + + Task 3:Phase 增量收口 —— 六道门禁(范围诊断归零 / eslint / prettier / CRLF / 符号零命中 / 基线增量与禁用路径)+ 人工复核 + `04-09-SUMMARY.md` 落盘与提交 + .planning/phases/04-render-adaptation/04-09-SUMMARY.md + Task 1 / Task 2 已闭环:范围内三文件诊断归零、符号零命中、CRLF 通过、基线文件仍可读 + + - .planning/phases/04-render-adaptation/04-07-SUMMARY.md:1-40,120-200(既有 SUMMARY 的元信息块、「执行前核对」「门禁输出摘要」「人工复核」「运行时 UAT 不可得声明」写法,可对齐版式) + - .planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md(A13/B8/C8/D9/E7/F8;本步是其 D 类 / F 类未决项经 D-43..D-50 裁定后的落地) + - .planning/phases/04-render-adaptation/04-CONTEXT.md(D-43..D-50 原文) + - .planning/phases/04-render-adaptation/04-09-PLAN.md(本计划门禁与禁令) + - dev.md(「其他要求」CRLF;「双端分离」) + + + (1) **重跑全部门禁并原样记录输出**:**门禁顺序为硬要求 —— ①..⑥ 全部在提交之前执行;本 action 内不得出现任何 `git commit`**(三份源文件由 Task 1 / Task 2 的每任务提交协议各自提交,本任务产物由本任务提交协议在 `` 六道门禁全绿之后提交)。① `pnpm exec vue-tsc --noEmit` —— 断言范围内三文件诊断数 = 0,并与 `%TEMP%/opencode/04-09-tsc-before.txt` 记录的前值(规划日 13)对比,确认全仓错误总行数**未因本计划新增**(范围内为 0;范围外既有诊断只记录不处置,D-33);② `pnpm exec eslint` 三文件;③ `pnpm exec prettier --check` 三文件;④ CRLF 三文件;⑤ `git grep` 符号零命中(`render/map/**` 内 `BlockCls`、`renderWithoutCheck`、`IBlockIdentifier`);⑥ 基线增量 + 禁用路径门禁 —— 三份目标文件当前内容哈希均**不同于** `%TEMP%/opencode/04-09-baseline-hashes.txt` 的基线(即确已被本计划改动;该判据**与文件是否已被每任务提交协议提交无关**,不得改用「`git status` 是否仍显示为已修改」),`git status --porcelain -- packages packages-user src` 相对 `%TEMP%/opencode/04-09-baseline.txt` 除三份目标文件外零新增 / 零消失 / 零状态变化,且 `material/**`、加载面、`packages/**`、`src/**` 零改动。 + + (2) **人工复核(不可自动化的部分)**:逐处比对 objective 的裁定编码表与代码实际形态 —— D-43 三处调用形态与实参、D-44 五处判定与一处解构键、D-45 勇士字面量、D-46 字段与取值来源;确认未新增 / 改名任何成员或字段、未改既有 jsDoc / 注释、未加 / 删 `// @ts-expect-error`、未引入 `import type`、未动 `package.json` / `pnpm-lock.yaml` / `tsconfig.json`、未新增测试;确认 `packages-user/client-modules/src/client.ts` 与 `render/map/moving.ts` / `render/map/types.ts` 零改动(范围外已知余留一律只报告、不处置)。 + + (3) **写 `.planning/phases/04-render-adaptation/04-09-SUMMARY.md`**(CRLF、与既有 SUMMARY 版式对齐):元信息块(日期 / 计划号 04-09 / wave 8 / 性质:代码改动·第二步实施 / 基准:`04-TEXTURE-INTERFACE-IMPACT.md` + `d36ea69`);逐条 D-43..D-50 的实际落点与实际行号;范围内诊断 13→0 的门禁输出摘要;全仓错误总行数前后对比(范围外既有诊断不归因本步);六道门禁输出摘要;人工复核结论;**无新命名声明**(仅一处局部解构绑定改名与既有枚举引入);**运行时 UAT 不可得声明**(渲染端无运行时测试设施,端到端验证为静态组合);**范围外已知余留(只报告、不处置)**:`packages-user/client-modules/src/client.ts:14` 的 `IAutotileProcessor` 与 `:16` 的 `AutotileProcessor` 是 `d36ea69` 删除 `IClientBase.autotile` 后遗留的孤立未使用 import,属 D-33 范围外、本计划未改动,须在 SUMMARY 中如实记录;**REND-01 / REND-02 仍保持 Pending** 的声明(本步仅为 REND-01 的 texture 子切片)。 + + (4) **提交(由每任务提交协议在 `` 六道门禁全绿之后执行;本 action 内不得出现任何 `git commit`,否则 `` 的基线门禁会在「已提交」的树上评估而失去意义)**:三份源文件已由 Task 1 / Task 2 的每任务提交协议各自提交,本任务只需提交本任务产物 `.planning/phases/04-render-adaptation/04-09-SUMMARY.md`(如路线图登记尚未随规划提交则一并包含);提交信息按仓库既有风格(如 `docs(04-09): record texture interface adaptation`);**不得**提交用户的任何并发改动(只 `git add` 本计划明确产出的路径)。 + + (5) 本任务只新增 `04-09-SUMMARY.md` 一个文件(提交动作由本任务提交协议在 `` 全绿后执行);不得改写 `04-01`..`04-08` 的任何产物。 + + 收口任务只产出簿记与提交,代码改动可整体回退;不引入契约或存储变更。 + + node -e "const{execSync}=require('child_process');let o='';try{o=execSync('pnpm exec vue-tsc --noEmit',{encoding:'utf8',maxBuffer:1<<28,stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'')}const scoped=['packages-user/client-modules/src/render/map/vertex.ts','packages-user/client-modules/src/render/map/renderer.ts','packages-user/client-modules/src/render/map/extension/hero.ts'];const hits=o.split(/\r?\n/).map(l=>l.replace(/\\/g,'/')).filter(l=>/error TS\d+/.test(l)&&scoped.some(f=>l.indexOf(f)>=0));if(hits.length){console.error('SCOPED TS ERRORS '+hits.length+' :: '+hits.slice(0,8).join(' @ '));process.exit(1)}console.log('OK zero scoped TS errors; total error-TS lines '+(o.match(/error TS\d+/g)||[]).length)" + non-zero exit, 或 stderr 含 "SCOPED TS ERRORS"(范围内诊断未归零) + node -e "const{execSync}=require('child_process');const files=['packages-user/client-modules/src/render/map/vertex.ts','packages-user/client-modules/src/render/map/renderer.ts','packages-user/client-modules/src/render/map/extension/hero.ts'];try{execSync('pnpm exec eslint '+files.join(' '),{encoding:'utf8',stdio:['ignore','pipe','pipe']});execSync('pnpm exec prettier --check '+files.join(' '),{encoding:'utf8',stdio:['ignore','pipe','pipe']});console.log('OK eslint + prettier')}catch(e){console.error('QUALITY GATE FAILED :: '+(e.stdout||'')+(e.stderr||''));process.exit(1)}" + non-zero exit, 或 stderr 含 "QUALITY GATE FAILED"(eslint 或 prettier 未通过) + node -e "const{execSync}=require('child_process'),fs=require('fs'),path=require('path'),crypto=require('crypto');const dir=path.join(process.env.TEMP||process.env.TMP,'opencode');const bp=path.join(dir,'04-09-baseline.txt'),hp=path.join(dir,'04-09-baseline-hashes.txt');if(!fs.existsSync(bp)||!fs.existsSync(hp)){console.error('MISSING BASELINE FILE');process.exit(1)}const target=['packages-user/client-modules/src/render/map/vertex.ts','packages-user/client-modules/src/render/map/renderer.ts','packages-user/client-modules/src/render/map/extension/hero.ts'];const sha=f=>crypto.createHash('sha256').update(fs.readFileSync(f)).digest('hex');const rec={};for(const l of fs.readFileSync(hp,'utf8').split(/\r?\n/).map(s=>s.trim()).filter(Boolean)){const i=l.indexOf(' ');rec[l.slice(i+2).replace(/\\/g,'/')]=l.slice(0,i)}const missing=target.filter(t=>!rec[t]);if(missing.length){console.error('BASELINE HASH MISSING '+missing.join(' ; '));process.exit(1)}const unchanged=target.filter(t=>sha(t)===rec[t]);if(unchanged.length){console.error('TARGET NOT CHANGED '+unchanged.join(' ; '));process.exit(1)}const base=fs.readFileSync(bp,'utf8').replace(/\s+$/,'');const rows=base.length?base.split(/\r?\n/).map(s=>s.replace(/\s+$/,'')):[];const now=execSync('git status --porcelain -- packages packages-user src',{encoding:'utf8'}).replace(/\s+$/,'');const cur=now.length?now.split(/\r?\n/).map(s=>s.replace(/\s+$/,'')):[];const added=cur.filter(l=>rows.indexOf(l)<0);const removed=rows.filter(l=>cur.indexOf(l)<0);const badAdded=added.filter(l=>!target.some(t=>l.endsWith(t)));const badRemoved=removed.filter(l=>!target.some(t=>l.endsWith(t)));if(badAdded.length||badRemoved.length){console.error('BASELINE DRIFT added=['+badAdded.join(' ; ')+'] removed=['+badRemoved.join(' ; ')+']');process.exit(1)}console.log('OK 3 targets differ from recorded hashes (commit-state independent) + baseline status preserved (dirty delta added '+added.length+' / removed '+removed.length+')')" + non-zero exit, 或 stderr 含 "MISSING BASELINE FILE" / "BASELINE HASH MISSING" / "BASELINE DRIFT" / "TARGET NOT CHANGED"(未落盘基线、目标文件未被本计划改动,或除三份目标文件外出现状态漂移 —— 注意本门禁以内容哈希判「目标已改」,与文件是否已被每任务提交协议提交无关) + node -e "const{execSync}=require('child_process');const raw=execSync('git status --porcelain -- packages packages-user src',{encoding:'utf8'});const rows=raw.replace(/\s+$/,'').length?raw.replace(/\s+$/,'').split(/\r?\n/):[];const forbidden=['packages-user/client-base/src/material/','packages-user/client-modules/src/fallback/','packages-user/client-base/src/load/','packages-user/client-base/src/types.ts','packages-user/client-modules/src/client.ts','packages-user/client-modules/src/core.ts','packages-user/client-modules/src/types.ts'];const bad=rows.filter(l=>{const p=l.slice(3).replace(/\\/g,'/');return p.indexOf('packages/')===0||p.indexOf('src/')===0||forbidden.some(f=>p.indexOf(f)===0)});if(bad.length){console.error('FORBIDDEN PATH TOUCHED '+bad.join(' ; '));process.exit(1)}console.log('OK no forbidden paths touched ('+rows.length+' entries)')" + non-zero exit, 或 stderr 含 "FORBIDDEN PATH TOUCHED"(`material/`、加载面、`packages/`、`src/` 或用户自有的客户端组合文件被改动) + node -e "const fs=require('fs');const p='.planning/phases/04-render-adaptation/04-09-SUMMARY.md';if(!fs.existsSync(p)){console.error('MISSING SUMMARY');process.exit(1)}const s=fs.readFileSync(p,'utf8');const need=['D-43','D-44','D-45','D-46','D-47','D-48','D-49','D-50','REND-01','REND-02','Pending','13','0','client.ts','IAutotileProcessor','余留'];const miss=need.filter(k=>s.indexOf(k)<0);if(miss.length){console.error('SUMMARY MISSING '+miss.join('|'));process.exit(1)}const lf=(s.match(/\n/g)||[]).length,crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf){console.error('SUMMARY NON-CRLF');process.exit(1)}console.log('OK summary content + CRLF')" + non-zero exit, 或 stderr 含 "MISSING SUMMARY" / "SUMMARY MISSING" / "SUMMARY NON-CRLF"(SUMMARY 未产出、缺裁定编号或需求状态声明、或非 CRLF) + + + - 六道门禁全部转绿并输出已原样记录:范围内诊断 = 0、eslint、prettier、CRLF、符号零命中、基线增量 + 禁用路径;六道门禁均在提交之前执行,且「目标文件已改动」以内容哈希断言(与是否已被每任务提交协议提交无关) + - 全仓错误总行数与 `04-09-tsc-before.txt` 对比后,范围内为 0 且范围外诊断未被本计划改动(只记录不处置) + - 人工复核逐条确认 D-43..D-50 的实际落点、无新命名、无注释改动、无 `@ts-expect-error` 增删、无 `import type`、无依赖 / 清单改动、无测试改动、`client.ts` 与 `moving.ts` / `render/map/types.ts` 零改动 + - `04-09-SUMMARY.md` 落盘(CRLF),含逐条裁定落点、13→0 门禁摘要、基线保持证据、无新命名声明、运行时 UAT 不可得声明、范围外已知余留(`client.ts:14` / `:16` 孤立未使用 import)只报告记录、`REND-01` / `REND-02` 保持 Pending 声明 + - 三份源文件已由 Task 1 / Task 2 的每任务提交协议提交,`04-09-SUMMARY.md` 已在 `` 全绿后提交;未提交任何用户并发改动;`04-01`..`04-08` 产物未被改写 + + Phase 4 第九个增量收口:三份地图渲染文件完成 TextureManager 新接口适应,范围内 texture 归属诊断由 13 归零;六道门禁(全部在提交之前执行)转绿;`04-09-SUMMARY.md` 落盘并在 `` 全绿后提交,其中记录范围外已知余留(`client.ts:14` / `:16`);`REND-01` / `REND-02` 仍 Pending + + + + + +## Trust Boundaries + +| Boundary | Description | +|----------|-------------| +| 渲染端消费者 → `@user/client-base` 素材接口 | 本步把消费者从旧形状 / 被删符号迁到现行接口;边界在编译期(`vue-tsc`)而非运行时,无不可信输入跨入 | +| 构建期依赖 → 仓库 | 本步不新增依赖,`package.json` / `pnpm-lock.yaml` / `tsconfig.json` 零改动 | + +## STRIDE Threat Register + +| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan | +|-----------|----------|-----------|----------|-------------|-----------------| +| T-04-09-01 | Tampering | `render/map/vertex.ts` / `renderer.ts` / `extension/hero.ts` 消费者 | medium | mitigate | 旧成员 `cls` / 被删方法残留会导致「静默取到 undefined / 类型伪装」;由 `vue-tsc` 范围内诊断归零门禁 + `git grep` 符号零命中门禁双重点验(D-43 / D-44) | +| T-04-09-02 | Tampering | `MapRenderer.autotile` 取值来源 | medium | mitigate | 若仍自建处理器会导致与 `manager.autotile` 状态分裂;D-46 强制改为 `manager.autotile`,并以「构造器无自建调用 + 字段声明与 `IMapRenderer` 签名未变」的人工复核确认 | +| T-04-09-03 | Elevation of Privilege | 范围外文件(`material/`、加载面、`packages/`、`src/`) | high | mitigate | 禁令 + `git status` 禁用路径门禁 + 基线增量门禁:除三份目标文件外零 delta;发现越界即失败 | +| T-04-09-04 | Denial of Service | 构建期类型门禁 | low | accept | 仓库既有无关诊断大量存在(D-33),门禁只断言范围内三文件;范围外诊断按既有基线记录,不由本计划处置 | +| T-04-09-05 | Information Disclosure | 本步触及的贴图 / 素材元数据 | low | accept | 改动仅接口成员名与判定条件,不新增数据出口、不记录敏感信息 | +| T-04-09-06 | Repudiation | 变更可追溯性 | low | mitigate | 原子提交三份源文件 + `04-09-SUMMARY.md`;SUMMARY 逐条记录裁定落点与实际行号 | +| T-04-09-SC | Tampering | npm/pnpm 安装(供应链) | high | mitigate | 本步零依赖新增;`package.json` / `pnpm-lock.yaml` 列入禁用路径门禁;如出现安装需求即暂停并告知用户(计划禁令) | + + + +- 范围内(`render/map/vertex.ts` + `render/map/renderer.ts` + `render/map/extension/hero.ts`)texture 归属 TS 诊断:13 → 0。 +- `pnpm exec vue-tsc --noEmit` 全仓错误总行数不因本计划上升(范围内为 0;范围外既有诊断只记录,D-33)。 +- `pnpm exec eslint <三文件>` 与 `pnpm exec prettier --check <三文件>` 通过。 +- CRLF 三文件门禁通过。 +- `render/map/**` 内旧符号(`BlockCls`、旧「跳过检查」调用)与被删标识符(`IBlockIdentifier`)零命中。 +- `git status --porcelain -- packages packages-user src` 相对 Task 1 状态基线除三份目标文件外零漂移,且三份目标文件内容 SHA-256 均已不同于 Task 1 记录的内容哈希(判据与提交状态无关);`material/**`、加载面、`packages/**`、`src/**` 零改动。 +- 人工复核:D-43..D-50 逐条落点、无新命名、无既有注释改动、无 `@ts-expect-error` 增删、无 `import type`、无依赖 / 清单 / 测试改动。 +- 运行时 UAT 不可得(渲染端无运行时测试设施)—— 如实声明,不以无法运行的门禁冒充通过。 + + + +- 13 条范围内 texture 归属诊断归零,且三份文件不新增任何诊断。 +- D-43 / D-44 / D-45 / D-46 精确落地(逐处可核);D-47 / D-48 / D-49 / D-50 的边界全部满足。 +- 无新命名、无既有注释改动、无依赖 / 清单 / 测试改动、无范围外改动。 +- `04-09-SUMMARY.md` 落盘并提交;`REND-01` / `REND-02` 保持 Pending。 + + + +Create `.planning/phases/04-render-adaptation/04-09-SUMMARY.md` when done + diff --git a/.planning/phases/04-render-adaptation/04-CONTEXT.md b/.planning/phases/04-render-adaptation/04-CONTEXT.md index e2aa6aa..a410298 100644 --- a/.planning/phases/04-render-adaptation/04-CONTEXT.md +++ b/.planning/phases/04-render-adaptation/04-CONTEXT.md @@ -82,6 +82,19 @@ - **D-41:** 「加载相关」的排除边界(用户界定):**加载本身**,以及**向 `TextureManager` 中添加素材的内容**(均由用户自行适配新加载系统)。 - **D-42:** `packages-user/client-base/src/material/` 文件夹已由用户**重构完毕**;AI **只修改消费者**(如地图渲染)。**若某接口被删除且无替代,必须向用户反馈**,不得自行发明替代方案。 +### 下一个目标:TextureManager 新接口适应·**第二步(实施)**(用户裁定,2026-09-23;待规划) +> 本步的输入 = 第一步只读清点产物 `.planning/phases/04-render-adaptation/04-TEXTURE-INTERFACE-IMPACT.md`(A13 / B8 / C8 / D9 / E7 / F8)。以下 D-43..D-50 为用户对该台账 D / F 两类未决项的逐条裁决。 + +- **D-43:** `renderWithoutCheck` 的替代 = **直接用新 `render(tile, connection)`**。用户原话:现在 `render` 已经进行了简化,性能比原本的 `renderWithoutCheck` 更好。3 处调用点(`render/map/vertex.ts:461`、`render/map/vertex.ts:874`、`render/map/renderer.ts:1253`)**全部改为 `render`**,不得保留或新增「跳过检查」入口。 +- **D-44:** `BlockCls` 的替代 = **`TileType`(`@user/data-common`)**。自动元件判定(`render/map/renderer.ts:1252`/`:1263`、`render/map/vertex.ts:517`/`:561`/`:872`)统一走 `tileType === TileType.Autotile`;**自动元件不需要做布局类型(`AutotileType`)判断**。用户明确:**不为旧 `BlockCls` 值做一一映射**——该类型尚未完全定稿、后续还会改,不得为了凑齐对应关系而发明映射表。 +- **D-45:** `render/map/extension/hero.ts:167` 的 `tileType` 取值 = **`TileType.Unknown`**。用户澄清:**D-18 指的是数据层的 `HeroRendering` 对象,不是渲染端的勇士渲染代码**,故 `render/map/extension/hero.ts` **不受 D-18 排除**,属本步范围。 +- **D-46:** `MapRenderer` **不再自建 `AutotileProcessor`**;**保留** `readonly autotile: IAutotileProcessor` 字段(`render/map/types.ts`,`IMapRenderer` 签名不变),**构造器内赋值为 `manager.autotile`**(用户原话:「全换成 manager 上的」)。 +- **D-47:** `ITextureManager.textures` / `tileStore` / `tiles` 的职责边界**不在本步解决**——地图背景已走现有 `manager.getTile`(`ITextureGetter` 未变),本步**只做接口适应**,不重新设计存储取值。 +- **D-48:** `textures` 的**写入侧归用户自行处理的加载系统**;**AI 不得修改 `packages-user/client-base/src/material/` 下的任何内容**(沿用并再次确认 D-42)。 +- **D-49:** **不考虑 legacy 兼容**——本次大重构的目的就是删除 legacy。凡「是否破坏 legacy 插件面」一类顾虑(对应 04-08 台账 `#04-08-D-08` / `#04-08-F-07`)**一律作废,不作为本步约束或验收项**。 +- **D-50:** `IBlockIdentifier` 已被用户删除,**其所有用法一并删除;本步范围内不得出现该接口**(其消费点 `packages-user/client-modules/src/fallback/load.ts` 属加载面,归用户)。 +- **本步范围(据 D-38..D-50 与 D-33 收敛):** AI 渲染端适配对象 = `packages-user/client-modules/src/render/map/vertex.ts`、`render/map/renderer.ts`、`render/map/extension/hero.ts`(台账中 texture 归属 13 条错误),连带 `render/map/moving.ts` / `render/map/types.ts` 的类型面;**排除**加载面(`fallback/load.ts`、`client-base/src/load/**`)、`packages-user/client-base/src/material/**`、`packages/`、`src/`、legacy 兼容。目标:texture 归属 23 条中属 AI 面的 13 条归零;**不追平全仓既有类型错误**(D-33)。`REND-01` / `REND-02` 仍 Pending——本步仅为 REND-01 的 texture 子切片。 + ### the agent's Discretion - D-07 的「影响」字段具体写法、「多余旧路径」是否需要进一步细分,交由 AI 在对账执行时按实际情况把握,但不得据此扩大范围。 diff --git a/.planning/state.json b/.planning/state.json index 5aa5366..15876a8 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-23T04:55:40.008Z" + "updated_at": "2026-09-23T06:46:17.246Z" }