docs(04-06): create material interface impact plan (read-only, D-30/D-31/D-32 step 1)

This commit is contained in:
unanmed 2026-09-21 19:53:14 +08:00
parent 4e305e392a
commit dbc733637c
2 changed files with 472 additions and 1 deletions

View File

@ -181,7 +181,8 @@ Plans:
3. 移动端(窄屏)布局下,同一场景正常显示且可操作
4. 数据端与渲染端保持双端分离——数据端无 DOM,仍可在 Node 环境跑回放验证
**Plans**: 4/4 plans executed(第一步·只读对账已完成;第二步·勇士移动接口探索(只读)已完成;第三步·勇士本体适配已执行;第四步·勇士渲染修正(04-03 人工审查反馈 D-24..D-28,D-29 修订为注入 `IFaceManager`)已规划,待执行 — 04-CONTEXT D-01/D-02/D-13/D-17/D-18/D-19/D-20/D-21/D-22/D-24..D-29;其余渲染适配与移动端/桌面端双布局待后续增量另行规划)
**Plans**: 5/5 plans executed(04-01..04-05)+ 1 planned(04-06,material 接口影响范围清点·只读,未执行)
**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`,第二步(实施)待用户审阅后另行规划。
Plans:
**Wave 1**
@ -200,6 +201,11 @@ Plans:
- [x] 04-04-PLAN.md — 勇士渲染修正(04-03 人工审查反馈 D-24..D-29):`render/map/extension/{hero,types,manager}.ts` 注入 `IFaceManager`(`faceManager`;`degrade`/`next` 走 `Dir4FaceHandler`、`movement` 走勇士 `mover.faceHandler`(Dir8)——D-24/D-29)并替换三个弃用 helper;`IMapExtensionManager.addHero(state, layer, faceManager)` 新增参数(D-29,改 `types.ts` 接口签名);拆分 `MapHeroLocationHook` / `MapHeroMoverHook` 且 `onSetPos` 无条件(D-25)、补 `AnimDir`(D-26)、改 `@motajs/animate` 的 `ExcitationCurve2D`(D-27)、保留存量 `state` 用法(D-28);不新增数据端接口,排除 D-18/D-19/D-22
**Wave 5** *(blocked on Wave 4 completion)*
- [x] 04-05-PLAN.md — 收口 `#04-01-M-06` / `#04-01-M-09`:状态栏 9 个数值属性从失效的 `getHeroStatusOn` 改经 `client.hero.attribute.getFinalAttribute(...)` 读取(并移除 `main.tsx:28` 失效的 `@ts-expect-error`);新增模块内不导出类 `RendererLayerHook`(`Partial<IMapLayerHooks>`)把逐图层 `onUpdateArea` / `onUpdateBlock` 转发到 `MapRenderer.updateLayerArea` / `updateLayerBlock`,并在 `setLayerState` / `updateLayerList` 两条路径注册与注销(M-06 使用 `client` 单例经用户裁定为 D-23 的显式例外)— **2026-09-20 已执行**(本次补登)
- [ ] 04-06-PLAN.md — material 接口影响范围清点(**只读**,D-30/D-31/D-32 第一步):以 `4e305e3` 后的 `client-base/src/{types.ts,material/types.ts}` 为基准,清点 `packages-user` 内对已移除 7 符号(`IBigImageReturn` / `isBigImage` / `getBigImage` / `getIfBigImage` / `getBigImageByAlias` / `setBigImage` / `bigImageStore`)、3 个改名接口、新增 `textures` 与 4 处 `ICoreStateExtended` 约束未实现、以及所有 big-image 残留(含 `material/manager.ts` 与 `render/elements/cache.ts`)的引用,按 A 已移除 / B 改名 / C 约束未实现 / D big-image 残留 / E 范围外命中(`src/`、`packages/` 仅报告)/ F 未确定 六类分组,每行精确到符号 + `file:line`;产出 `04-MATERIAL-INTERFACE-IMPACT.md`,生产代码零改动,第二步实施待用户审阅后另行规划
**UI hint**: yes
### Phase 5: Legacy 移植

View File

@ -0,0 +1,465 @@
---
phase: 04-render-adaptation
plan: 6
type: execute
wave: 5
depends_on:
- 04-01
- 04-02
- 04-03
- 04-04
files_modified:
- .planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md
autonomous: false
requirements:
- REND-01
- REND-02
estimate:
tokens: 78000
raw_tokens: 78000
tasks: 4
confidence: low
must_haves:
truths:
- "**计划号冲突(必须在 Task 0 原样汇报)**:`packages-user`/phase 目录下**已存在** `.planning/phases/04-render-adaptation/04-05-PLAN.md` 与 `04-05-SUMMARY.md`(第五个增量·`#04-01-M-06` / `#04-01-M-09` 收口,2026-09-20 已执行且改动在工作树中:`RendererLayerHook`、`getFinalAttribute`),故本增量按顺序号落为 **04-06**(`depends_on` 仍列 04-01..04-04 以保持 `wave: 5`,与编排给定的 wave 一致;04-05 的已完成改动属本计划的**基线**,不是本计划的产出)。`04-05-PLAN.md` / `04-05-SUMMARY.md` 一律**不得改写、不得覆盖、不得改号**;Task 0 必须把该冲突原样汇报并请用户确认编号(若用户要求改号,须先裁决 04-05 存量产物的归属,再由用户指定文件名,不得自行覆盖)。"
- "**本计划的第一个 block 点是 Task 0 的 `blocking-human` 汇报关卡**:先向用户汇报「要解决的问题(material 接口在 `4e305e3` 后的影响面未清点,剩余实现/消费者仍在引用已删除或已改名的成员,且新增约束未实现)、大致解决方案(只读清点,唯一交付物 `04-MATERIAL-INTERFACE-IMPACT.md`,11 节结构 / A–F 六类分列 / 每行精确到符号 + file:line)、`4e305e3` 基准的三类差异(移除 7 个符号、改名 3 个接口、新增 `textures` 与 4 处 `ICoreStateExtended` 约束)、范围与禁令、**计划号冲突(04-05 已被占用 → 本计划为 04-06)**、验证方式与只读证明」,然后必须**停下等待**用户明确回复「可以执行」四个字;收到该回复前不得执行任何任务、不得派发任何执行子代理。无论触发方式(`/gsd-execute-phase`、`/gsd-manager`、`/gsd-autonomous`、子代理/执行器派发)或配置模式(`.planning/config.json` 的 `mode: yolo`、`workflow.auto_advance`)如何,均不得自动放行(AGENTS.md)"
- "**只读硬约束(生产代码零改动)**:本 run 生产源码改动为 **0**——`git status --porcelain -- packages packages-user src` 在 Task 1 步骤 (0a) 起始逐行原样记录进 `## 方法`(规划日 2026-09-21 实测为「(无)」),Task 1 / Task 3 门禁断言其后**逐条一致**(不新增、不消失、状态码不变);`pnpm exec vue-tsc --noEmit` 的**错误总行数**与 **material 相关错误行数**在 Task 1 步骤 (0b) 记录(规划日实测 **199 / 20**),Task 3 门禁重跑并断言两个数字**完全一致**。`files_modified` 只含 `04-MATERIAL-INTERFACE-IMPACT.md` 一个交付物;本 PLAN 与 `04-06-SUMMARY.md` 属计划/执行簿记,不算生产改动。"
- "**A 类(已移除符号的剩余引用·引用侧)**:必须逐一登记 D-31 点名的 **7 个已移除符号** `IBigImageReturn` / `isBigImage` / `getBigImage` / `getIfBigImage` / `getBigImageByAlias` / `setBigImage` / `bigImageStore`,每个符号给出**引用侧 `file:line`**;`client-modules` 侧 9 处调用点一条不漏——`getIfBigImage` 7 处(`render/map/extension/door.ts:36,46`;`render/map/extension/hero.ts:239,364,428`;`render/map/renderer.ts:1250`;`render/map/vertex.ts:546`)、`bigImageStore` 1 处(`render/map/renderer.ts:652`)、`getBigImage` 1 处(`render/map/renderer.ts:653`);无调用点的 `setBigImage` / `isBigImage` / `getBigImageByAlias` 必须**显式登记为「调用点 0 处」**(不遗漏、不臆造);`IBigImageReturn` 的引用侧为 `client-base/src/material/manager.ts:20`(import 自 `./types`,该符号已不在基准导出中)。"
- "**B 类(改名的剩余引用)必须给出机器证据的「0 命中」结论**:`IMaterialManager` → `ITextureManager`、`IMaterialGetter` → `ITextureGetter`、`IMaterialAliasGetter` → `ITextureAliasGetter`,三个旧名在**源码面**(`packages`、`packages-user`、`src`,排除 `node_modules`)的当日命中数必须为 **0**,并原样记录核对命令 `git grep -n -E \"IMaterialManager|IMaterialGetter|IMaterialAliasGetter\" -- packages packages-user src` 的输出;`.planning/graphs/graph.json` 与 `.planning/graphs/.last-build-snapshot.json` 中仍存在的 `IMaterialManager` / `IMaterialGetter` 标签属**过期工装态**(快照时间戳 2026-09-07,早于 `4e305e3` 的 2026-09-21),只作 E 类登记,**不得**算作源码命中、**不得**作为基准。"
- "**C 类(新增与收紧约束的未实现)必须逐实现类登记 5 项,并带当日实测错误码**:① `ITextureManager.textures`(基准 `client-base/src/material/types.ts:262-263`)未实现于 `MaterialManager`(`material/manager.ts:38`,成员停在 `tileStore`/`tilesetStore`/`imageStore`/`assetStore`/`bigImageStore`,无 `textures`)→ 实测 `client.ts:84` 报 TS2739;②③④⑤ `ICoreStateExtended`(定义源 `packages-user/data-state/src/types.ts:45-49`)被 `ITextureManager`(`material/types.ts:261`)、`IAutotileProcessor`(`:104`)、`IAssetBuilder`(`:430`)、`ITrackedAssetData`(`:462-463`)四处 `extends`,而四个实现类 `MaterialManager`(`manager.ts:38`)、`AutotileProcessor`(`autotile.ts:42`)、`AssetBuilder`(`builder.ts:12`)、`TrackedAssetData`(`builder.ts:99`)**均无 `state` 成员** → 实测 `manager.ts:38`/`autotile.ts:42`/`builder.ts:12`/`builder.ts:99` 报 TS2420,构造点 `manager.ts:86`(`new AssetBuilder(this)`)、`builder.ts:91`(`TrackedAssetData`)、`client.ts:85`(`new AutotileProcessor(this.materials)`)、`render/map/renderer.ts:234`(`new AutotileProcessor(manager)`)报 TS2741。`state` 的语义来源与注入方式**不在本步裁定**(进 F 类)。"
- "**D 类(big-image 实现残留)必须逐成员给 `file:line`,且与 A 类分列、互相引用**:`MaterialManager` 上的 `bigImageStore`(`material/manager.ts:43`)、`bigImageData`(`:56`)、`bigImageId`(`:77`)、`setDefaultFrame` 对 `bigImageData` 的耦合(`:233-235`)、`setBigImage`(`:552-573`)、`isBigImage`(`:575-577`)、`getBigImage`(`:579-581`)、`getBigImageByAlias`(`:583-587`)、`getIfBigImage`(`:589-593`)、`IBigImageReturn` 类型引用(`:20`、`:556`、`:568`);以及**不走 `ITextureManager` 的 legacy 概念残留** `client-modules/src/render/elements/cache.ts`(`:36`、`:48`、`:144`、`:153`、`:157`、`:159-160`、`:190`、`:215`、`:243`、`:276`,经 `core.material.images.images[bigImage]` 与 `enemys[id].bigImage` 读旧全局)。分列规则写死:**A 记「消费者调用点」,D 记「client-base 内已不属接口的实现残留」**;同一成员两侧都有时两条都记并互相引用。"
- "**E 类(范围外命中·仅报告)与 in-scope 严格分开**:`src/types/declaration/enemy.d.ts:65`(`bigImage?: ImageIds`)、`core.d.ts:1286`(同)、`map.d.ts:354`(`bigImage: HTMLImageElement`)与 `:1443`(`_getBigImageInfo`)、`status.d.ts:391`(`bigImage: HTMLImageElement`)——只登记事实,**不得**写成待修改项、不得提出修改建议;`packages/` 为**零命中**(须附核对命令与结论,与 D-32「`packages` 不纳入、理应不受影响」一致);`.planning/graphs/{graph.json,.last-build-snapshot.json}` 的过期标签;`packages-user/client-base/package.json` **未声明** `@user/data-state` 依赖而 `material/types.ts:9` 与 `client-base/src/types.ts:3` 已 `import`(登记为事实,是否算问题**交由用户判断**)。"
- "**F 类(未能从阅读确定·未猜测)非空且只登记不判定**,至少覆盖:① `getIfBigImage` 的语义替代是否成立(新基准只提供 `getTile` / `getRenderable` / `getImage`,`door.ts` 的大图开门帧数语义、`hero.ts` 的贴图取值、`vertex.ts` 的顶点裁剪是否可由 `getTile` 等价承接);② `ITextureManager.textures`(`types.ts:262`)应由哪个现有 store(`tileStore` 还是新建)承担;③ 四个实现类缺失的 `state` 从何处注入(`ClientCore extends CoreState`,构造点是否传 `this`);④ `cache.ts` 的 legacy bigImage 路径属本 material 增量还是 Phase 5 legacy 清退边界;⑤ `setDefaultFrame` 与 `bigImageData` 的耦合在新概念下是否有对位;⑥ `.planning/graphs/*` 是否需要重建。**不得**把本类任何一条写成错配、缺失或残留。"
- "**`## 基准变更(4e305e3)` 与 `## 类型门禁实测基线` 两节必须齐备**:前者逐条记录三类差异(移除 7 符号 / 改名 3 接口 / 新增 `textures` 与 4 处 `ICoreStateExtended` 约束)并点名定义源 `packages-user/data-state/src/types.ts:45-49`,核对方式为 `git show 4e305e3 -- packages-user/client-base/src/types.ts packages-user/client-base/src/material/types.ts` 与当日工作树逐条比对;后者逐字记录 `pnpm exec vue-tsc --noEmit` 的 `- **错误总行数**:<n>` 与 `- **material 相关错误行数**:<n>`(规划日实测 199 / 20,执行日以实测为准)并逐行列出 material 相关错误,同时单列与本改动无关的既有错误(data-state / data-base / data-system 的测试与寻路、`client-base/src/load/loader.ts` 与 `client-modules/src/{core.ts,render/ui/load.tsx}` 的加载系统重构结果)——**不得**把它们算作本次 material 改动的后果。"
- "**第二步(修改工作)不被规划**:文档只清点、分类、分组,**不含**修复方案、不含代码改动建议、不产出 `04-07-PLAN.md` 或任何新 `*-PLAN.md`;`## 处置` 明写「第一步只读清点完成;第二步(material 接口适应实施)待用户审阅本文件后另行规划」(D-32),并保留第一条清点步骤与第六条材料的原话口径。"
- "**REND-01 / REND-02 在本 run 后仍保持 Pending**:本计划只清点 material 接口的影响面,既未实施 material 适应,也未实施移动端 / 桌面端双布局(见 assumptions 的两条 FLAGGED ASSUMPTION)。"
- "**验证可运行且不臆造通过**:本计划的「运行结果」= 只读门禁(porcelain 基线逐条一致 + 类型错误总数与 material 子集一致)+ 静态内容门禁(11 节结构、A–F 六类行数下限与关键词、每行 ≥2 个 `file:line` 锚点、B 类 0 命中、E 类只报告口径)+ 既有计划保护门禁(`04-05-PLAN.md` / `04-05-SUMMARY.md` porcelain 为空)+ 计划文件范围门禁(不得出现 `04-07+`)+ CRLF 门禁 + 人工代码复核;本步为只读清点,**无运行时行为可观察**,故不得以任何无法运行的门禁冒充通过。"
artifacts:
- path: .planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md
provides: "`4e305e3` material 接口基准在 `packages-user` 全量的只读影响台帐:元信息块 + 背景 / 方法 / 基准变更(4e305e3)/ 类型门禁实测基线 / A 已移除符号的剩余引用 / B 改名的剩余引用 / C 新增与收紧约束的未实现 / D big-image 残留(实现与消费者)/ E 范围外命中(仅报告)/ F 未能从阅读确定(未猜测)/ 处置;每行含接口或成员名 + 命中处 `file:line` + 基准侧锚点 + 问题描述 + 影响 + 置信,供用户审阅后另行规划第二步"
key_links:
- "`packages-user/client-base/src/material/types.ts`(`4e305e3` 后的权威基准)↔ `packages-user/client-base/src/types.ts:4,18`(`ITextureManager` 是 `IClientBase.materials` 的唯一公共面):基准中已无 `IBigImageReturn` / `isBigImage` / `getBigImage` / `getIfBigImage` / `getBigImageByAlias` / `setBigImage` / `bigImageStore`;`ITextureManager extends ITextureGetter, ITextureAliasGetter, ICoreStateExtended` 并新增 `readonly textures: ITextureStore`(`:260-263`);`IAutotileProcessor extends ICoreStateExtended`(`:104`);`IAssetBuilder extends ICoreStateExtended`(`:430`);`ITrackedAssetData extends IDirtyTracker<Set<number>>, ICoreStateExtended`(`:462-463`)"
- "`packages-user/data-state/src/types.ts:45-49`(`ICoreStateExtended { readonly state: ICoreState }`)↔ `material/types.ts:9` 的 import ↔ 四个实现类 `manager.ts:38` / `autotile.ts:42` / `builder.ts:12` / `builder.ts:99` 缺 `state`(实测 TS2420),构造点 `manager.ts:86` / `builder.ts:91` / `client.ts:85` / `render/map/renderer.ts:234`(实测 TS2741)"
- "`MaterialManager implements ITextureManager`(`material/manager.ts:38`)↔ 其剩余 big-image 实现面(`manager.ts:20,43,56,77,233-235,552-593`)↔ 实测 `error TS2305`(`manager.ts:20`)/ `error TS2420`(`manager.ts:38`)/ `error TS2741`(`manager.ts:86`)"
- "`client-modules` 的消费者调用点(`render/map/extension/door.ts:36,46`、`render/map/extension/hero.ts:239,364,428`、`render/map/renderer.ts:652,653,1250`、`render/map/vertex.ts:546`)↔ 基准 `ITextureManager` 上已不存在的成员 → 实测 9 条 `error TS2339` / `TS2551`"
- "构造 / 接线面(`client-modules/src/client.ts:84` 的 `new MaterialManager()`、`client.ts:85` 的 `new AutotileProcessor(this.materials)`、`render/map/renderer.ts:234` 的 `new AutotileProcessor(manager)`、`manager.ts:86` 的 `new AssetBuilder(this)`、`builder.ts:28` 的 `new TrackedAssetData(materials, this)`)—— `state` 与 `textures` 的注入/实现面必须在本清点文件中登记,本步不作裁定"
- "`git status --porcelain -- packages packages-user src`(Task 1 步骤 (0a) 逐行记录)↔ Task 1 / Task 3 门禁的逐条一致断言 ↔ 只读(生产代码零改动)证明"
- "`pnpm exec vue-tsc --noEmit` 的错误总行数与 material 相关子集(Task 1 步骤 (0b) 记录、规划日实测 199 / 20)↔ Task 3 门禁的重跑一致断言 ↔ 只读证明"
assumptions:
- "**FLAGGED ASSUMPTION(specless fallback)— REND-01 edge unresolved:** 未提供 REND-01(渲染端适配新数据层接口)的验收边界;本计划**不发明**阈值,只交付 material 接口影响的只读清点(D-30 / D-31 / D-32 第一步)。用户须在推进第二步与其余渲染适配前补齐 REND-01 的边界判据。"
- "**FLAGGED ASSUMPTION(specless fallback)— REND-02 edge unresolved:** 未提供 REND-02(移动端与桌面端双布局)的验收边界;本计划不涉及双布局,也不发明其验收阈值。"
- "**计划号冲突(未确定项,不猜):** `.planning/phases/04-render-adaptation/04-05-PLAN.md` 与 `04-05-SUMMARY.md` 已存在(第五个增量·`#04-01-M-06` / `#04-01-M-09`,2026-09-20 已执行:其改动 `RendererLayerHook`(`render/map/renderer.ts:435,1775`)与 `client.hero.attribute.getFinalAttribute`(`render/ui/main.tsx:103+`)在工作树中)。编排给定的「04-05」与该存量冲突,故本计划按顺序号落为 **04-06**。用户若要求其它编号,须先裁决存量 04-05 产物的归属再改名,不得覆盖。"
- "**执行前必须复核(未确定项,不猜):** Task 1 步骤 (0a)/(0b) 必须在动手前打开当日 HEAD 的工作树逐条核对:① 7 个已移除符号确不在 `material/types.ts` 的现行导出/成员集合中;② 三个改名后的名称 `ITextureManager` / `ITextureGetter` / `ITextureAliasGetter` 与新增 `readonly textures: ITextureStore`、四处 `extends ICoreStateExtended` 的现行位置;③ `ICoreStateExtended` 的定义源(`data-state/src/types.ts:45-49`);④ 四个实现类(及其所在行)确实缺 `state`、`MaterialManager` 确实缺 `textures`;⑤ `client-modules` 侧 9 处调用点的**当日行号**(`door.ts` / `hero.ts` / `renderer.ts` / `vertex.ts`);⑥ `cache.ts` 与 `src/types/declaration/*` 的命中行号;⑦ `packages/` 的零命中结论;⑧ `pnpm exec vue-tsc --noEmit` 的当日总数与 material 子集。**任一与规划日数值不同即以当日实测为准**写入文档并在 SUMMARY 中说明差异,不得按本计划的规划日数值硬写(规划日 2026-09-21、HEAD = `4e305e3`)。"
- "**material / 非 material 错误划分口径须用户确认:** 规划日把 `client-base/src/load/loader.ts` 的 6 条(`LoadAudioProcessor` / `LoadFontProcessor` / `LoadImageProcessor` / `LoadTextProcessor` / `LoadZipProcessor` / `IMotaDataLoader` 未导出)与 `client-modules/src/core.ts:6` 的 1 条、`render/ui/load.tsx` 的 8 条判为**非 material**(属加载系统重构 `cc434a6` 的结果),其余 20 条判为 material 相关。该口径是**规划日的人工归类**,须用户在审阅时确认;若用户判定其中某条应归 material,执行者按裁定重新归类并同步两个数字与 `## 处置` 的口径说明,不得改动其它事实。"
- "**无运行时验证可得(诚实降级):** 本步为只读清点,不产生任何运行时行为(不启动渲染、不改代码、不新增测试),因此承载体是「只读门禁 + 类型门禁一致性 + 静态内容门禁 + 人工代码复核」。**不得**以任何无法运行的门禁冒充通过。"
- "**用户可能有执行日的并发未提交改动:** 一律以 Task 1 步骤 (0a) 当日实测为基线(含 `04-CONTEXT.md` 的既有未提交改动,须逐字保留),**不得回滚 / 暂存 / 提交 / 修改**用户的任何未提交改动;若基线中出现 `packages`/`packages-user`/`src` 下的条目,属于本次清点范围内的既有改动,须一并记录并原样保留。"
- "**`.planning/graphs/*` 是过期工装态:** 快照时间戳为 2026-09-07(早于 `4e305e3` 的 2026-09-21),其中仍标注 `IMaterialManager` / `IBigImageReturn` / `getBigImage` 等已被删除的符号;该文件**不得**作为基准或命中依据,只作 E 类登记(是否重建由用户决定,见 F 类)。"
prohibitions:
- "不得在用户明确回复「可以执行」四个字之前执行本计划的任何任务、或派发任何执行子代理(AGENTS.md 的第一个 block 点约束;不因触发方式或 `yolo` / `auto_advance` 模式而放宽)。"
- "不得修改任何生产源码:`packages/**`、`packages-user/**`、`src/**` 的生产文件一律零改动;不得新增 / 删除 / 改写任何 `.ts` / `.tsx` 文件;不得触碰用户 committed 的基准接口(`client-base/src/material/types.ts` 与 `client-base/src/types.ts` 一字不动)。"
- "不得改写 / 覆盖 / 改号 `04-05-PLAN.md`、`04-05-SUMMARY.md`(既有第五个增量产物);除本 `04-06-PLAN.md`、`04-MATERIAL-INTERFACE-IMPACT.md` 与 `04-06-SUMMARY.md` 外不得写任何其它文件。"
- "不得规划或实施第二步:不得给出修复方案、不得给出代码改动建议、不得产出 `04-07+` 或任何新 `*-PLAN.md`。"
- "不得修改任何既有 jsDoc / 注释文本(AGENTS.md);不得新增或删除 `// @ts-expect-error` 指令。"
- "不得修改 `.planning/phases/04-render-adaptation/04-CONTEXT.md`(当前工作树内有用户未提交改动,须逐字保留、不得回滚 / 暂存 / 提交)。"
- "不得把 `packages/` 与 `src/` 的命中写成待修改项、不得为其提出修改建议;E 类只登记事实与核对命令。"
- "不得把 A / B / C / D / E 混为一类:`manager.ts` 的实现残留归 D、`client-modules` 的调用点归 A,二者必须分列并互相引用;`cache.ts` 的 legacy 概念路径归 D 并明确标注「不走 `ITextureManager`」。"
- "不得把「类型错误」当作「接口设计结论」:`textures` 该由哪个 store 承担、`state` 从何处注入、`getIfBigImage` 的替代是什么,一律进 F 类,不得自行裁定、不得写成缺失或残留。"
- "不得引入 `core.*` / `client.*` legacy 适配或清退建议(D-12 / D-23);不得为 `cache.ts` 的 legacy 路径提出清退计划(归属见 F 类,属 Phase 5 边界)。"
- "不得新增依赖、不得修改 `package.json` / `pnpm-lock.yaml` / `tsconfig.json`;出现安装需求即暂停并告知用户。"
- "不得新增、删除或修改任何测试文件与既有测试。"
- "不得以「无运行时」为由弱化验证:每条 `<automated>` 必须有 `<fails_when>`;确实无法运行的门禁必须显式声明为不可得并降级为静态 + 人工,不得伪造绿。"
---
<objective>
Phase 4 第六个增量(用户裁定 2026-09-21 · D-30 / D-31 / D-32 **第一步**)—— **material 接口影响范围清点(只读)**。
> **计划号冲突(须用户确认)**:本计划按编排指令「只规划 material 接口影响清点」执行,但**顺序号落为 `04-06`**。原因:`.planning/phases/04-render-adaptation/04-05-PLAN.md` 与 `04-05-SUMMARY.md` 已存在且已执行(第五个增量·`#04-01-M-06` / `#04-01-M-09` 收口,2026-09-20;其改动 `RendererLayerHook` 与 `getFinalAttribute` 在工作树中),编排给定的「04-05」与该存量产物冲突。为不破坏既有执行记录与 `04-05-SUMMARY.md` 的对应关系,本计划**不改写也不覆盖 04-05**,改用下一个空闲编号 04-06;`depends_on` 仍列 04-01..04-04 以保持 `wave: 5`。若用户要求其它编号,须先裁决 04-05 存量产物的归属(改号或保留),再由用户指定文件名。
**要解决的问题:**
1. **接口已改、实现与消费者未跟上(D-30/D-31)**:用户以提交 `4e305e3 refactor(type): material types.` 重写了 Texture 底层管理器并**删除 `big-image` 概念**(旧样板概念,新引擎不再需要,旧兼容可无痛丢弃)。该提交已从接口删除 `IBigImageReturn` / `isBigImage` / `getBigImage` / `getIfBigImage` / `getBigImageByAlias` / `setBigImage` / `bigImageStore`,把 `IMaterialManager`→`ITextureManager`、`IMaterialGetter`→`ITextureGetter`、`IMaterialAliasGetter`→`ITextureAliasGetter`,并新增 `ITextureManager.textures` 与四处 `ICoreStateExtended` 约束。**剩余未适应的实现与消费者由本任务处理(第一步先清点)。**
2. **影响面未知,不能直接动手**:用户明示「影响面可能很大」(D-31),并裁定**分两步**——第一步收集影响范围(只读清点),第二步进行修改;第二步骤第一步结果与用户审阅后再规划(D-32)。没有第一手的范围台账就直接改,会按旧形状改错或漏改。
3. **big-image 残留必须在清点中报告(D-32 边界确认)**:用户明确要求「若发现其余 big-image 残留,必须在清点文档中报告」。
4. **范围与基准已钉死(D-32)**:范围限 `packages-user`(`src/` 或 `packages/` 内命中**仅报告、不修改**);基准为 `packages-user/client-base/src/types.ts` 与 `packages-user/client-base/src/material/types.ts`(`4e305e3` 后);第一步产出 `.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md`(只读,允许派只读子代理)。
**大致解决方案:** 只做**只读清点**,产出唯一交付物 `.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md`。文档 11 节:`## 背景` / `## 方法` / `## 基准变更(4e305e3)` / `## 类型门禁实测基线` / `## A 已移除符号的剩余引用` / `## B 改名的剩余引用` / `## C 新增与收紧约束的未实现` / `## D big-image 残留(实现与消费者)` / `## E 范围外命中(仅报告)` / `## F 未能从阅读确定(未猜测)` / `## 处置`。A–E 每节一张固定列序的表 `| ID | 接口/成员名 | 命中处 | 基准侧锚点 | 问题描述 | 影响 | 置信 |`,**每行至少两个 `file:line` 锚点**(命中处 + 基准侧),一条记录只归一类,跨类拆条并互相引用;F 类逐条登记静态阅读无法定论项(只登记不判定)。
**分列规则(写死,防止类别互相冒充):**
| 类别 | 含义 | 归属规则 |
|---|---|---|
| A | 已移除**接口成员**的**引用点**(消费者侧) | `client-modules` 对 `manager.*` 的调用点 + `manager.ts:20` 的 `IBigImageReturn` import |
| B | 改名后的旧接口名剩余引用 | 源码面命中数;本次应为 0,须给机器证据 |
| C | 新增 / 收紧约束在实现类上未实现 | 基准侧 = 接口声明行;命中侧 = 实现类声明行与构造点 |
| D | **不在接口成员层面**的 big-image 实现/概念残留 | `client-base` 内已不属接口的实现成员 + 走 legacy 全局的 `cache.ts` 路径 |
| E | 范围外(`src/`、`packages/`、`.planning/graphs/*`、依赖声明) | 只报告,不修改、不建议 |
| F | 未能从阅读确定 | 只登记,不得写成错配 / 缺失 / 残留 |
**规划日实测(作为起点与门禁下限,执行日以当日实测为准):**
| 类别 | 规划日实测结果(2026-09-21,HEAD `4e305e3`,工作树) |
|---|---|
| 基准变更 | 移除 7 符号;改名 3 接口;新增 `readonly textures: ITextureStore` 与 4 处 `extends ICoreStateExtended` |
| A(移除引用) | `client-modules` 侧 **9** 处调用点(`getIfBigImage` 7、`bigImageStore` 1、`getBigImage` 1)+ `manager.ts:20` 的 `IBigImageReturn` import |
| B(改名) | 源码面 **0** 命中(仅 `.planning/graphs/*` 过期标签) |
| C(约束未实现) | **5** 项:`textures` 1 项 + `state` 4 个实现类;实测 `client-base` 7 条 + `client-modules` 13 条 = **20** 条 material 相关类型错误 |
| D(big-image 残留) | `MaterialManager` **10** 项 + `cache.ts` legacy 路径 1 项 |
| E(范围外) | `src/types/declaration/*` **5** 处;`packages/` **0** 命中;`.planning/graphs/*` 过期标签;`client-base/package.json` 未声明 `@user/data-state` |
| F(未确定) | ≥ 6 项(见 must_haves) |
| 类型门禁 | `pnpm exec vue-tsc --noEmit` 错误总行数 **199**(其中 material 相关 **20**,非 material 既有 **179**) |
**范围与硬边界:** 被查对象为 `packages-user` 全量(含新包 `packages-user/client-system`);基准为 `client-base/src/material/types.ts` 与 `client-base/src/types.ts`;`packages/` **不纳入**(D-32,理应不受本改动影响,本次实测零命中);`src/` 命中**仅报告**;`data-*` 仅作基准(D-04),但 `ICoreStateExtended` 的定义源 `data-state/src/types.ts:45-49` + 须点名(它由 `4e305e3` 首次加入)。生产源码零改动;本步不规划第二步。
**两步走(D-32):** 本 run = **第一步(只读清点)**;**第二步(material 接口适应实施)不在本 run 规划**,待用户审阅本文件后另行规划。
## Source Audit(多源覆盖审计)
| SOURCE | ID | Feature/Requirement | Plan | Status | Notes |
|--------|----|---------------------|------|--------|-------|
| GOAL | — | Phase 4:「渲染端通过新数据层接口驱动,并同时支持移动端与桌面端布局」 | 04-06 | COVERED(增量·material 只读清点子切片) | 本 run 只产出 material 接口影响台账;其余渲染适配与双布局属后续增量(REND-02 见 assumptions) |
| REQ | REND-01 | 渲染端适配新数据层接口 | 04-06 | COVERED(子切片·只读;需求保持 Pending) | material 接口(`client-base`)是渲染端唯一素材公共面;本 run 只清点其影响面,不实施适配;验收边界未提供 → assumptions 的 FLAGGED ASSUMPTION |
| REQ | REND-02 | 渲染端同时支持移动端与桌面端布局 | 04-06 | COVERED(不涉及) | 本 run 范围不含双布局;REND-02 验收边界未提供 → assumptions 的 FLAGGED ASSUMPTION |
| RESEARCH | — | (本阶段无 `04-RESEARCH.md`) | — | N/A | Phase 4 未产出 RESEARCH.md;等价输入为 04-01 / 04-02 账本与 `4e305e3` 的接口改动 |
| CONTEXT | D-30 | 下一个任务 = 完成 material 相关的接口适应(用户已改接口,剩余未适应的实现与消费者由本任务处理) | 04-06 | COVERED(第一步) | Task 1/2/3 清点剩余未适应的实现与消费者;实施属第二步(未规划) |
| CONTEXT | D-31 | 根因:重写 Texture 底层管理器、删除 `big-image` 概念;已删 7 符号、改名 3 接口、新增 `textures` 与 `ICoreStateExtended`;影响面可能很大 | 04-06 | COVERED | `## 基准变更(4e305e3)` 逐条记录三类差异;A/C/D 三类逐条登记影响,并实测 20 条 material 相关类型错误 |
| CONTEXT | D-32 | 分两步:第一步收集影响范围(只读清点,产出 `04-MATERIAL-INTERFACE-IMPACT.md`);第二步再规划。边界:范围限 `packages-user`,`packages` 不纳入;基准为 `client-base` 的 `types.ts` / `material/types.ts`;发现其余 big-image 残留必须报告;`src/` / `packages/` 命中仅报告不修改 | 04-06 | COVERED | 只读、唯一交付物、D 类报告全部 big-image 残留、E 类只报告;第二步明写未规划 |
| CONTEXT | D-07 | 每条记录精确到接口名 + 所属文件 + 问题描述 + 影响 | 04-06 | COVERED(粒度) | 固定列序 + 每行 ≥2 个 `file:line` 锚点 + 门禁机器判定 |
| CONTEXT | D-09 | 允许派只读子代理扫描(不得修改任何文件) | 04-06 | COVERED | Task 2 允许只读子代理回传事实表;子代理不得写文件;最终由主执行者门禁统一验证 |
| CONTEXT | D-12 | `core.*` 属旧引擎适配,归第五阶段 | 04-06 | COVERED(边界) | `cache.ts` 的 `core.material.*` 路径只登记为 legacy 事实并进 F 类归属未定;不提出清退计划 |
| CONTEXT | D-18 | `HeroRendering` 相关暂不处理 | 04-06 | COVERED(边界) | `extension/hero.ts` 的 `getIfBigImage` 命中只作 material 接口引用点登记,不涉及 `HeroRendering` 语义裁定 |
| CONTEXT | D-19 | 移动控制由用户手动修改,AI 暂不处理 | 04-06 | COVERED(排除) | `action/move.ts` 与移动控制不在本计划的命中面枚举 |
| CONTEXT | D-22 | 跟随者暂缓 | 04-06 | COVERED(排除) | 跟随者族不在本计划范围 |
| CONTEXT | D-23 | 不使用 `client` / `state` 全局单例(仅约束新增/后续) | 04-06 | COVERED | 本计划零生产改动、不新增代码;`state` 的注入方式只进 F 类不作裁定 |
| CONTEXT | D-01..D-06, D-08, D-10, D-11, D-13..D-17, D-20, D-21, D-24..D-29 | 前序决策与已执行增量 | 04-01..04-05 | COVERED(前序计划) | 已由 04-01..04-05 落地;本计划是其后的新增量 |
| CONTEXT | Deferred Ideas | 阶段 4 后续步骤(适配实施、双布局);`packages` 全部、`entry-client`、`legacy-plugin-*`、`data-fallback` 不在第一步范围 | — | DEFERRED | 未写入任何任务;`packages/` 仅作 E 类对照扫描 |
## Estimate Basis(估算依据)
`estimate-calibration` 返回 `factor: 1`、`applied: false`、`sample_count: 0`、`confidence: low`(项目无历史样本),故 `tokens == raw_tokens` 并如实标注 `confidence: low`。按「执行者实际触及量」估算(`estimate.tasks: 4` = Task 0 汇报关卡 + 三个工作任务):Task 0 只读汇报并等待用户确认(约 5k,不写文件);Task 1(tracer)起始基线(porcelain + `pnpm exec vue-tsc --noEmit` 全量,约 1–3 分钟)+ 交付文档骨架 + `## 基准变更(4e305e3)` + `## 类型门禁实测基线` + A 类首条端到端切片(约 30k);Task 2 横向补齐 A/B/C/D/E/F 六类(约 27k);Task 3 定稿 + 五道门禁 + 人工复核 + `04-06-SUMMARY.md`(约 11k);固定开销约 5k;合计约 78k。
</objective>
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
<context>
@.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-RENDER-INTERFACE-AUDIT.md
@.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md
@.planning/phases/04-render-adaptation/04-02-PLAN.md
@.planning/phases/04-render-adaptation/04-05-PLAN.md
@.planning/phases/04-render-adaptation/04-05-SUMMARY.md
@.planning/phases/04-render-adaptation/04-PATTERNS.md
@AGENTS.md
@dev.md
</context>
<tasks>
<task type="checkpoint:decision" gate="blocking-human">
<name>Task 0: 预执行汇报关卡 —— 汇报 material 接口影响清点(D-30/D-31/D-32 第一步)的范围、基准、A–F 六类分列规则与唯一交付物、计划号冲突(04-05 已被占用 → 本计划 04-06)与只读证明方式,等待用户回复「可以执行」</name>
<precondition>用户对 material 接口适应的裁定 D-30 / D-31 / D-32 已写入 `.planning/phases/04-render-adaptation/04-CONTEXT.md`(以文件内实际存在 D-30..D-32 为准);`.planning/phases/04-render-adaptation/04-05-PLAN.md` 与 `04-05-SUMMARY.md` 已存在(第五个增量已执行)</precondition>
<files>(只读汇报,不修改任何文件)</files>
<read_first>
- AGENTS.md(「任何计划的执行,其第一个 block 点必须是向我汇报计划内容并等待我确认」;只有明确回复「可以执行」四个字之后才允许开始执行;「如果发现任何问题,永远不要擅自将其视为问题并记录,必须经由我确认」)
- .planning/phases/04-render-adaptation/04-CONTEXT.md(D-30 / D-31 / D-32 裁定原文;D-07 粒度、D-09 子代理、D-12 / D-18 / D-19 / D-22 / D-23 边界;Deferred Ideas)
- .planning/phases/04-render-adaptation/04-05-PLAN.md 与 04-05-SUMMARY.md(计划号冲突的证据:既有第五个增量已执行,其改动仍在工作树中)
- .planning/phases/04-render-adaptation/04-06-PLAN.md(本计划全文:objective 的三类差异与分列规则 / tasks / 禁令 / 门禁)
- packages-user/client-base/src/material/types.ts(基准::104 `IAutotileProcessor extends ICoreStateExtended`、:196 `ITextureGetter`、:228 `ITextureAliasGetter`、:260-263 `ITextureManager extends ... ICoreStateExtended` 与 `readonly textures`、:430 `IAssetBuilder extends ICoreStateExtended`、:462-463 `ITrackedAssetData extends ... ICoreStateExtended`;7 个已移除符号确不在其中)
- packages-user/client-base/src/types.ts:1-11(`ITextureManager` 作为 `IClientBase.materials` 的唯一公共面)与 packages-user/data-state/src/types.ts:45-49(`ICoreStateExtended` 定义源)
- packages-user/client-base/src/material/manager.ts:16-24,38-43,56,77,86,231-237,552-593(实现残留与缺失成员)
- packages-user/client-modules/src/render/map/extension/door.ts:30-56、packages-user/client-modules/src/render/map/extension/hero.ts:235-242,360-368,424-432、packages-user/client-modules/src/render/map/renderer.ts:649-657,1244-1252、packages-user/client-modules/src/render/map/vertex.ts:544-548(9 处消费者调用点)
- packages-user/client-modules/src/render/elements/cache.ts:30-48,144-160,190,215,243,276(legacy bigImage 概念残留)
</read_first>
<action>
**停下并向用户汇报,等待明确回复后才继续;汇报本身只读,不写任何文件、不改任何生产代码、不派发子代理。**
① **要解决的问题(D-30 / D-31 / D-32)**:用户以 `4e305e3 refactor(type): material types.` 重写了 Texture 底层管理器、**删除 `big-image` 概念**(旧样板概念,新引擎不再需要,旧兼容可**无痛丢弃**),并已改完受影响内容的一部分;**剩余未适应的实现与消费者**尚未处理。用户明示「影响面可能很大」,故裁定**分两步**:第一步只读清点影响范围,第二步再规划修改。**本 run 只做第一步**。用户并明确要求:**若发现其余 big-image 残留,必须在清点文档中报告**。
② **`4e305e3` 基准的三类差异(须逐条汇报)**:
- **移除 7 个符号**:`IBigImageReturn`、`isBigImage`、`getBigImage`、`getIfBigImage`、`getBigImageByAlias`、`setBigImage`、`bigImageStore`(接口面已无;`bigImageStore` 亦已从 `ITextureManager` 删除)。
- **改名 3 个接口**:`IMaterialManager` → `ITextureManager`、`IMaterialGetter` → `ITextureGetter`、`IMaterialAliasGetter` → `ITextureAliasGetter`。
- **新增 / 收紧约束**:`ITextureManager` 新增 `readonly textures: ITextureStore`(`material/types.ts:262-263`),且由 `ITextureManager`(`:261`)、`IAutotileProcessor`(`:104`)、`IAssetBuilder`(`:430`)、`ITrackedAssetData`(`:462-463`)四处 `extends ICoreStateExtended`(定义源 `packages-user/data-state/src/types.ts:45-49`:`readonly state: ICoreState`)。
③ **规划日实测的影响面(作为起点与门禁下限)**:`pnpm exec vue-tsc --noEmit` 当前 **199** 条错误,其中 **20** 条由本次 material 接口改动直接造成(`client-base` 7 条 + `client-modules` 13 条);其余 179 条属用户既有的加载系统重构 / 寻路 / 测试等,**不算**本改动的后果(该划分口径须用户确认)。具体:A 类 `client-modules` 侧 **9** 处调用点(`getIfBigImage` 7 处:`door.ts:36,46`、`hero.ts:239,364,428`、`renderer.ts:1250`、`vertex.ts:546`;`bigImageStore` 1 处:`renderer.ts:652`;`getBigImage` 1 处:`renderer.ts:653`)+ `manager.ts:20` 的 `IBigImageReturn` import;B 类源码面 **0** 命中;C 类 **5** 项(`textures` 未实现 + 4 个实现类缺 `state`);D 类 `MaterialManager` **10** 项 + `cache.ts` legacy 路径 1 项;E 类 `src/types/declaration/*` **5** 处、`packages/` **0** 命中、`.planning/graphs/*` 过期标签、`client-base/package.json` 未声明 `@user/data-state`。
④ **大致解决方案**:只读清点,唯一交付物 `.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md`;11 节结构(`背景` / `方法` / `基准变更(4e305e3)` / `类型门禁实测基线` / `A 已移除符号的剩余引用` / `B 改名的剩余引用` / `C 新增与收紧约束的未实现` / `D big-image 残留(实现与消费者)` / `E 范围外命中(仅报告)` / `F 未能从阅读确定(未猜测)` / `处置`);A–E 每节固定列序表(`ID | 接口/成员名 | 命中处 | 基准侧锚点 | 问题描述 | 影响 | 置信`),每行 ≥2 个 `file:line` 锚点;**分列规则**:A = 消费者调用点、D = `client-base` 内已不属接口的实现残留与 `cache.ts` 的 legacy 概念路径(二者分列并互相引用);F 类只登记不判定。重型整文件读取可按 **D-09** 派**只读**子代理(仅回传事实表,不得写文件)。
⑤ **范围与硬边界**:被查 = `packages-user` 全量(含新包 `packages-user/client-system`);基准 = `client-base/src/material/types.ts` + `client-base/src/types.ts`(`4e305e3` 后);`packages/` **不纳入**(D-32,理应不受影响,本次实测零命中);`src/` 命中**仅报告、不修改**;`data-*` 仅作基准且须点名 `ICoreStateExtended` 的定义源。**生产源码零改动**;唯一写入为上述交付文档(外加本 PLAN 与 `04-06-SUMMARY.md` 两份簿记)。
⑥ **计划号冲突(必须原样告知用户)**:phase 目录下**已存在** `04-05-PLAN.md` 与 `04-05-SUMMARY.md`(第五个增量·`#04-01-M-06` / `#04-01-M-09` 收口,2026-09-20 已执行;其改动 `RendererLayerHook`(`render/map/renderer.ts:435,1775`)与 `client.hero.attribute.getFinalAttribute`(`render/ui/main.tsx:103+`)在工作树中)。编排给定的「04-05」与该存量冲突,故本计划按顺序号落为 **04-06**,`depends_on` 列 04-01..04-04 以保持 `wave: 5`;**04-05 一律不得改写 / 覆盖 / 改号**。请用户确认 04-06 这一编号(或指定其它编号,但须先裁决 04-05 存量产物的归属)。
⑦ **验证方式与只读证明(须原样告知)**:本步为只读清点,**无运行时行为可观察**。证明方式 = ①**只读门禁**:`git status --porcelain -- packages packages-user src` 在 Task 1 起始逐行记录(规划日实测为「(无)」),Task 1 / Task 3 断言其后逐条一致;②**类型门禁一致性**:`pnpm exec vue-tsc --noEmit` 的错误总行数与 material 子集(规划日 199 / 20)记录后,Task 3 重跑断言完全一致;③**静态内容门禁**:11 节结构、A–F 行数下限与关键词、每行 ≥2 个 `file:line` 锚点、B 类 0 命中、E 类只报告口径;④**既有计划保护门禁**:`04-05-PLAN.md` / `04-05-SUMMARY.md` 的 porcelain 为空;⑤**计划范围门禁**:不得出现 `04-07+`;⑥**CRLF 门禁**;⑦**人工代码复核**。**不冒充任何无法运行的门禁。**
⑧ **第二步不被规划**:本 run **不含**修复方案、不含代码改动建议、不产出 `04-07+`;第二步(material 接口适应实施)待用户审阅本清点文件后另行规划(D-32)。
⑨ **关卡性质(必须原样告知用户)**:无论触发方式(`/gsd-execute-phase`、`/gsd-manager`、`/gsd-autonomous`、子代理/执行器派发)或配置模式(`.planning/config.json` 的 `mode: yolo`、`workflow.auto_advance`)如何,**执行者/子代理都必须在此停下并等待**用户明确回复「可以执行」四个字;未收到这四个字前不得执行任何任务、不得派发执行子代理,也不得以「自动化模式」为由自动放行。
⑩ **失败语义(等同硬门禁,非自动命令)**:用户未回复「可以执行」即继续,或回复的是其它任何内容(含修订意见、沉默、`yolo` / `auto_advance` 模式下的自动推进、或子代理/执行器的自行放行),一律视为**未通过本关卡**:必须立即停止执行,不派发任何执行子代理,保持只读、不写任何文件,直到用户在对话中明确回复「可以执行」四个字后方可继续。本条只以本 `<action>` 与 `<acceptance_criteria>` 表达,不写成 XML 失败声明元素(人工关卡没有可运行的自动命令可与之绑定)。
⑪ **若用户给出修订意见**(含不同意编号 / 不同意分列规则 / 不同意 material 与「非 material」的划分口径 / 不同意范围或基准):退出本计划、修订 `04-06-PLAN.md` 后重新汇报,不得自行改道执行。
</action>
<decision>是否按本计划(material 接口影响范围只读清点:唯一交付物 `04-MATERIAL-INTERFACE-IMPACT.md`,11 节 / A–F 六类,范围限 `packages-user`,`src/` 与 `packages/` 命中仅报告,生产代码零改动,第二步不规划)开始执行;并确认本计划的编号为 **04-06**(`04-05` 已被既有已执行增量占用)</decision>
<context>
AGENTS.md 要求任何计划的第一个 block 点必须是向用户汇报计划内容并等待用户确认;本计划虽是只读清点,仍不得例外。`.planning/config.json` 为 `mode: yolo`(execute-plan 默认自动推进),因此该关卡必须显式声明为 `blocking-human` 并置 `autonomous: false`,否则计划层面不提供任何 block 点。此外 phase 目录内已存在已执行的 `04-05-PLAN.md` / `04-05-SUMMARY.md`,编排给定的编号与之冲突,必须在关卡中先行澄清,避免覆盖既有执行记录。
</context>
<options>
<option id="execute">
<name>可以执行</name>
<pros>按本计划执行:只读清点,零生产代码改动,唯一写入为交付文档 + 两份簿记;产出可供用户审阅并据此规划第二步 material 适配</pros>
<cons>无(只读;只读性与类型门禁一致性由 Task 1 / Task 3 机器判定)</cons>
</option>
<option id="execute-renumber">
<name>可以执行,但改用其它编号</name>
<pros>按用户指定的编号落地,避免后续引用混淆</pros>
<cons>需用户先裁决既有 `04-05-PLAN.md` / `04-05-SUMMARY.md` 的归属(保留或改号);本计划文件名随之调整</cons>
</option>
<option id="revise">
<name>修订方案 / 范围 / 分列规则后再执行</name>
<pros>避免带着错误的范围(如把 `packages/` 纳入)或错误的分列规则(把实现残留报成接口缺失)落地</pros>
<cons>退出本计划并修订 PLAN.md 后重新汇报,增加一轮往返</cons>
</option>
</options>
<resume-signal>回复「可以执行」四个字(或给出修订意见)</resume-signal>
<verify>
<human-check>用户在对话中明确回复「可以执行」四个字(人工核对用户回复原文;本关卡为人工关卡,无自动命令,失败语义写在 Task 0 的 action ⑩ 与 acceptance_criteria 中)</human-check>
</verify>
<acceptance_criteria>
- 用户回复中明确包含「可以执行」四个字
- 汇报内容涵盖:要解决的问题(D-30/D-31/D-32 第一步)、`4e305e3` 的三类差异(移除 7 符号 / 改名 3 接口 / 新增 `textures` 与 4 处 `ICoreStateExtended`)、规划日实测的影响面(20 条 material 相关类型错误与 A–F 六类的具体命中)、大致解决方案(11 节结构 + 固定列序 + A/D 分列规则 + F 只登记不判定)、范围与硬边界(`packages-user` 限界、`packages/` 不纳入、`src/` 仅报告、生产代码零改动)、唯一交付物与两步走
- 汇报中已原样告知**计划号冲突**:`04-05-PLAN.md` / `04-05-SUMMARY.md` 已存在且已执行,本计划编号为 04-06,04-05 不得改写/覆盖/改号,并请用户确认编号
- 汇报中已原样告知**验证方式与只读证明**(只读门禁 / 类型门禁一致性 / 静态内容门禁 / 既有计划保护门禁 / 计划范围门禁 / CRLF / 人工复核),并声明无运行时行为可观察
- 汇报中已原样告知**关卡性质**:执行者/子代理必须停下等待,不因触发方式或 `yolo` / `auto_advance` 模式自动放行
</acceptance_criteria>
<done>用户明确回复「可以执行」;在此之前未执行任何任务、未修改任何文件、未派发任何执行子代理</done>
</task>
<task type="tracer">
<name>Task 1(tracer):交付文档骨架 + 一条端到端清点切片 —— 起始基线(porcelain + `pnpm exec vue-tsc --noEmit`)+ 11 节结构 + `## 基准变更(4e305e3)` 三类差异定稿 + `## 类型门禁实测基线` 实测数字 + A 类 `#04-06-A-01` 双锚点实测,结构 / CRLF / 只读门禁转绿</name>
<precondition>Task 0 已闭环:用户在对话中明确回复「可以执行」四个字(未收到则不得开始本任务)</precondition>
<files>.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md</files>
<read_first>
- .planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md:1-23(元信息块与 `## 方法` 的既有写法,可直接对齐版式)与 :26-34(固定列序表的既有写法)
- .planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md:1-20(元信息块 5 行与「接口基准」声明块的写法)
- .planning/phases/04-render-adaptation/04-CONTEXT.md(D-30 / D-31 / D-32 原文;D-07 粒度;D-09 子代理;D-12 / D-18 / D-19 / D-22 / D-23 边界)
- .planning/phases/04-render-adaptation/04-06-PLAN.md(本计划全文:objective 的三类差异表与分列规则、tasks、禁令、门禁)
- packages-user/client-base/src/material/types.ts(整文件::104 / :196 / :228 / :260-263 / :430 / :462-463;确认 7 个已移除符号不在其中、`ITextureGetter` / `ITextureAliasGetter` 的现行成员集合)
- packages-user/client-base/src/types.ts:1-32(`ITextureManager` 作为 `IClientBase.materials` 的唯一公共面)
- packages-user/data-state/src/types.ts:1-52(`ICoreState` 与 `ICoreStateExtended` 的定义位置)
- packages-user/client-base/src/material/manager.ts:16-24,38-43,56,77,86,231-237,552-593(A 类首条切片与 D 类的命中处;`IBigImageReturn` import 在 `:20`)
- packages-user/client-modules/src/render/map/extension/door.ts:30-56(`getIfBigImage` 的两处调用,A 类 `#04-06-A-01` 的命中处)
- dev.md(「项目结构」的单向依赖;「命名规则」;「其他要求」的 CRLF 要求)
</read_first>
<action>
(0a) **起始基线快照(只读前置,先做这一步)**:执行 `git status --porcelain -- packages packages-user src`,把输出**原样逐行**记为「只读起始基线」写入 `## 方法`:先写 bullet `- **只读起始基线(packages / packages-user / src)**:`,其后逐行抄录输出(每条一行,行首为两字符状态码;输出为空则写 `(无)`)。规划日 2026-09-21 实测该输出为**空**(工作树内唯一的未提交改动是 `.planning/phases/04-render-adaptation/04-CONTEXT.md`,不在该路径集合内)。**不得回滚、暂存、提交或修改**任何既有改动;若当日实测出现 `packages` / `packages-user` / `src` 下的条目,逐条记录并原样保留(属本次清点范围内的既有改动,需在 `## 方法` 注明)。同时记下 `git rev-parse HEAD` 的当日值写入元信息块。
(0b) **类型门禁实测基线(只读前置,与 (0a) 同批完成)**:执行 `pnpm exec vue-tsc --noEmit`,把完整输出的**错误总行数**与**material 相关错误行数**两个数字写入 `## 类型门禁实测基线`:bullet 逐字写 `- **错误总行数**:<N>` 与 `- **material 相关错误行数**:<M>`,其中 N = `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||'')}console.log((o.match(/error TS\d+/g)||[]).length)"` 的输出值(与 Task 3 门禁使用**同一正则**,保证可逐字比对)。规划日 2026-09-21 实测 **N = 199**、**M = 20**。同时在该节**逐行列出全部 material 相关错误**(`file:line` + `error TSxxxx` + 原文摘要)并按以下规划日归类核对:`client-base` 7 条 —— `material/autotile.ts:42` TS2420、`material/builder.ts:12` TS2420、`material/builder.ts:91` TS2741、`material/builder.ts:99` TS2420、`material/manager.ts:20` TS2305、`material/manager.ts:38` TS2420、`material/manager.ts:86` TS2741;`client-modules` 13 条 —— `client.ts:84` TS2739、`client.ts:85` TS2741、`render/map/extension/door.ts:36` 与 `:46` TS2339、`render/map/extension/hero.ts:239` / `:364` / `:428` TS2339、`render/map/renderer.ts:234` TS2741、`render/map/renderer.ts:652` TS2551、`render/map/renderer.ts:653` TS2551、`render/map/renderer.ts:1250` TS2339、`render/map/vertex.ts:546` TS2339。**单列**与本改动无关的 179 条既有错误(`data-state` / `data-base` / `data-system` 的测试与寻路、`client-base/src/load/loader.ts` 6 条与 `client-modules/src/core.ts:6` / `render/ui/load.tsx` 8 条属用户加载系统重构),并明写该「material / 非 material」划分口径**须用户确认**(见 assumptions)。**以当日实测为准**,与规划日数值不同即按实测写入并在 `## 处置` 说明差异,不得按规划日数值硬写。
(1) **新建交付文档** `.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md`,先写标题与 6 行元信息块:`**日期:**`(当日日期);`**范围:**` 被查对象 `packages-user` 全量(含新包 `packages-user/client-system`),`packages/` 不纳入(D-32),`src/` 命中仅报告;`**性质:**` 只读清点(D-30 / D-31 / D-32 第一步),未修改任何生产代码;`**接口基准:**` `packages-user/client-base/src/material/types.ts` 与 `packages-user/client-base/src/types.ts`(`4e305e3` 后)以及 `packages-user/data-state/src/types.ts`(`ICoreStateExtended` 定义源);`**基准提交:**` `4e305e3 refactor(type): material types.`(当日工作树 HEAD);`**下一步:**` 第二步(material 接口适应实施)未规划,待用户审阅本文件后另行规划(D-32)。**全文使用 CRLF 换行**(dev.md「换行使用 CRLF 格式」);若写入工具产出 LF,用 `node -e "const fs=require('fs');const p='.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md';fs.writeFileSync(p,fs.readFileSync(p,'utf8').replace(/\r?\n/g,'\r\n'))"` 统一转换后落盘;Task 2 / Task 3 追加内容同样保持 CRLF,不得出现孤立 LF。
(2) 写 `## 背景`:D-30(下一个任务 = 完成 material 相关接口适应;用户已自行改接口 `4e305e3` 并改完受影响的一部分,剩余未适应的实现与消费者由本任务处理)、D-31(根因:重写 Texture 底层管理器、删除 `big-image` 概念;已删 7 符号 / 改名 3 接口 / 新增 `textures` 与 `ICoreStateExtended`;影响面可能很大)、D-32(分两步,本步只读清点;范围限 `packages-user`、`packages` 不纳入;基准为 `client-base` 的 `types.ts` / `material/types.ts`;**发现其余 big-image 残留必须报告**;`src/` / `packages/` 命中仅报告不修改)。明写本步**只清点、不修改、不规划第二步**。
(3) 写 `## 方法`((0a) 的「只读起始基线」bullet 也在本节):枚举两侧面 ——(a)**基准面** = `packages-user/client-base/src/material/types.ts`(`ITextureManager` / `ITextureGetter` / `ITextureAliasGetter` / `IAutotileProcessor` / `IAssetBuilder` / `ITrackedAssetData` / `IMaterialData` / `IMaterialFramedData` 等现行成员)+ `packages-user/client-base/src/types.ts`(`IClientBase.materials`)+ `packages-user/data-state/src/types.ts`(`ICoreStateExtended`);(b)**命中面** = `packages-user` 全量(`client-base` / `client-modules` / `client-system` / `entry-*` / `legacy-plugin-*` / `data-fallback`),另对 `packages/` 与 `src/` 做**对照扫描**(只登记、不修改)。判定口径固定为 **A 已移除符号的引用点 / B 改名的剩余引用 / C 新增与收紧约束的未实现 / D big-image 残留(实现与消费者)/ E 范围外命中 / F 未能从阅读确定** 六类,**一条记录只归一类**,跨类拆条并互相引用;**A 与 D 的分列规则写死**:A 记「消费者调用点」(`client-modules` 对 `manager.*` 的调用 + `manager.ts:20` 的类型 import),D 记「`client-base` 内已不属接口的实现残留」与「走 legacy 全局的 `cache.ts` 概念路径」。证据纪律:每行含 `接口/成员名 + 命中处 file:line + 基准侧锚点 file:line + 问题描述 + 影响 + 置信`,**每行至少两个锚点**;静态阅读不能定论者一律进 F 类,**不得写成错配 / 缺失 / 残留**。允许按 **D-09** 派**只读**子代理做重型整文件扫描(子代理只回传事实表「成员名 + 所属 `file:line` + 形态」,**不得写文件、不得贴大段源码**),最终由主执行者按 `file:line` 切片复核。同时记录 (0b) 的类型门禁命令与两个数字的口径(正则 `error TS\d+` 的匹配数)。
(4) 补齐 11 个二级标题(`## 背景` / `## 方法` / `## 基准变更(4e305e3)` / `## 类型门禁实测基线` 已在前几步写入,此处补齐其余 7 个占位,其后 Task 2 在同一骨架上横向补齐,不再改结构):`## A 已移除符号的剩余引用` / `## B 改名的剩余引用` / `## C 新增与收紧约束的未实现` / `## D big-image 残留(实现与消费者)` / `## E 范围外命中(仅报告)` / `## F 未能从阅读确定(未猜测)` / `## 处置`。
(5) **`## 基准变更(4e305e3)` 定稿(本任务的核心横向切片)**:以 `git show 4e305e3 -- packages-user/client-base/src/types.ts packages-user/client-base/src/material/types.ts` 为依据,并与当日工作树**逐条比对**后写入三类差异:① **移除 7 个符号** —— `IBigImageReturn`(原定义在 `material/types.ts`)、`isBigImage` / `getBigImage` / `getIfBigImage`(原在 `IMaterialGetter`,现 `ITextureGetter` 的成员集合不含)、`getBigImageByAlias`(原在 `IMaterialAliasGetter`,现 `ITextureAliasGetter` 不含)、`setBigImage`(原在 `IMaterialManager`,现 `ITextureManager` 不含)、`bigImageStore`(同上),每条附**现行基准侧锚点**(即该符号原所属接口在 `material/types.ts` 的现行行,例如 `ITextureGetter` `:196`、`ITextureAliasGetter` `:228`、`ITextureManager` `:260`);② **改名 3 个接口** —— `IMaterialManager` → `ITextureManager`(`:260`)、`IMaterialGetter` → `ITextureGetter`(`:196`)、`IMaterialAliasGetter` → `ITextureAliasGetter`(`:228`),并列出该提交内同步改名的消费者文件(`client-base/src/load/loader.ts`、`client-base/src/material/{autotile,builder,manager}.ts`、`client-modules/src/{client.ts,fallback/load.ts,render/map/moving.ts,render/map/renderer.ts,render/map/types.ts,types.ts}`);③ **新增 / 收紧约束** —— `ITextureManager` 新增 `readonly textures: ITextureStore`(`:262-263`,附 jsDoc 原文),以及四处 `extends ICoreStateExtended`(`ITextureManager` `:261` / `IAutotileProcessor` `:104` / `IAssetBuilder` `:430` / `ITrackedAssetData` `:462-463`),并**点名定义源** `packages-user/data-state/src/types.ts:45-49`(`readonly state: ICoreState`)。另记录同提交内的**相邻变更**(登记,不作本步裁定):`client-base/src/types.ts` 新增 `rafExcitation` / `excitationDivider`,新增 `packages-user/client-system` 包与 `IClientSystem`。**不得**修改任何被引用文件。
(6) **A 类首条切片(本任务唯一实测清点行)**:在 `## A 已移除符号的剩余引用` 下先写固定列序表头 `| ID | 接口/成员名 | 命中处 | 基准侧锚点 | 问题描述 | 影响 | 置信 |`,再写 `#04-06-A-01` 一行:接口/成员名 = `getIfBigImage`(原 `IMaterialGetter`/`ITextureGetter` 成员,`4e305e3` 已删);命中处 = `packages-user/client-modules/src/render/map/extension/door.ts:36,46`;基准侧锚点 = `packages-user/client-base/src/material/types.ts:196-226`(`ITextureGetter` 现行成员集合 `getTile` / `getBlockCls` / `getAsset` / `getTileset` / `getImage`,不含 `getIfBigImage`);问题描述 = `openDoor` / `closeDoor` 经 `this.renderer.manager.getIfBigImage(num)` 取值并读取 `data.frames` 驱动大图开门帧序,该成员已不在接口上;影响 = 运行时不可达(若该类型断言被绕过)且编译报 TS2339(`door.ts:36,46`);置信 = 高;依据 = 上述两处锚点 + 实测错误行。同一行内**不得**出现修复建议或替代写法(替代是否成立属 F 类)。同时在 `## 类型门禁实测基线` 中确证这两处错误行已逐字列出。
(7) 写 `## 处置` 初稿:本步只读、未改任何生产代码、`files_modified` 仅本文件;相对 (0a) 记录的只读起始基线零变化;本步**不规划第二步**(D-32),第二步(material 接口适应实施)待用户审阅本文件后另行规划;范围限 `packages-user`、`packages` 不纳入、`src/` 命中仅报告(D-32);`getIfBigImage` 的替代、`textures` 的承担者、`state` 的注入方式一律进 F 类不作裁定(D-23 亦约束新增代码不得取全局单例);`core.*` legacy 适配(D-12)与移动控制(D-19)、跟随者(D-22)、`HeroRendering` 语义(D-18)不属本步;`REND-01` / `REND-02` 保持 Pending。
(8) **本任务只写该交付文档一个文件**;不得改动任何被查源码(含用户既有的未提交改动),不得派发会写文件的子代理,不得新增计划文件。
</action>
<reversibility rating="reversible">交付文档是只读清点的记录,可随时修订或废弃;它不引入生产代码、公共接口或契约约束,因此这一步的任何取舍都不难回退。</reversibility>
<verify>
<automated>node -e "const fs=require('fs');const p='.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md';const t=fs.readFileSync(p,'utf8');const need=['## \u80cc\u666f','## \u65b9\u6cd5','## \u57fa\u51c6\u53d8\u66f4','## \u7c7b\u578b\u95e8\u7981\u5b9e\u6d4b\u57fa\u7ebf','## A \u5df2\u79fb\u9664\u7b26\u53f7','## B \u6539\u540d\u7684\u5269\u4f59\u5f15\u7528','## C \u65b0\u589e\u4e0e\u6536\u7d27\u7ea6\u675f','## D big-image','## E \u8303\u56f4\u5916\u547d\u4e2d','## F \u672a\u80fd\u4ece\u9605\u8bfb\u786e\u5b9a','## \u5904\u7f6e'];const miss=need.filter(h=>t.indexOf(h)<0);if(miss.length>0){console.error('MISSING HEADINGS '+miss.length+' :: '+miss.join(' ; '));process.exit(1)}if(t.indexOf('\u53ea\u8bfb\u8d77\u59cb\u57fa\u7ebf')<0){console.error('MISSING READONLY BASELINE');process.exit(1)}if(!/4e305e3/.test(t)){console.error('MISSING BASELINE COMMIT');process.exit(1)}console.log('OK headings '+need.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "MISSING HEADINGS" / "MISSING READONLY BASELINE" / "MISSING BASELINE COMMIT"(11 个二级标题未齐备,或 `## 方法` 未记录只读起始基线,或未登记基准提交)</fails_when>
<automated>node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md','utf8');const seg=(h)=>{const i=t.indexOf(h);if(i<0)return '';const rest=t.slice(i+h.length);const j=rest.search(/\r?\n## /);return j<0?rest:rest.slice(0,j)};const bs=seg('## \u57fa\u51c6\u53d8\u66f4');const need=['IBigImageReturn','isBigImage','getBigImage','getIfBigImage','getBigImageByAlias','setBigImage','bigImageStore','IMaterialManager','IMaterialGetter','IMaterialAliasGetter','ITextureManager','ITextureGetter','ITextureAliasGetter','textures','ICoreStateExtended'];const miss=need.filter(k=>bs.indexOf(k)<0);if(miss.length>0){console.error('BASELINE SECTION MISSING '+miss.join('|'));process.exit(1)}if(!/[A-Za-z0-9_\-\/]+\.ts:\d+/.test(bs)){console.error('BASELINE SECTION WITHOUT ANCHOR');process.exit(1)}console.log('OK baseline tokens '+need.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "BASELINE SECTION MISSING" / "BASELINE SECTION WITHOUT ANCHOR"(`## 基准变更(4e305e3)` 未覆盖 7 个已移除符号 / 3 个改名接口 / `textures` / `ICoreStateExtended`,或该节无任何 `file:line` 锚点)</fails_when>
<automated>node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md','utf8');const a=t.match(/\u9519\u8bef\u603b\u884c\u6570\uff1a\s*(\d+)/);const b=t.match(/material \u76f8\u5173\u9519\u8bef\u884c\u6570\uff1a\s*(\d+)/);if(!a||!b){console.error('MISSING TSC BASELINE FIGURES');process.exit(1)}if(Number(b[1])<1||Number(b[1])>Number(a[1])){console.error('TSC BASELINE FIGURES INCONSISTENT '+a[1]+' '+b[1]);process.exit(1)}const n=(t.match(/error TS\d+/g)||[]).length;if(n<5){console.error('TOO FEW MATERIAL ERROR ANCHORS '+n);process.exit(1)}console.log('OK tsc total '+a[1]+' material '+b[1]+' listed '+n)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "MISSING TSC BASELINE FIGURES" / "TSC BASELINE FIGURES INCONSISTENT" / "TOO FEW MATERIAL ERROR ANCHORS"(未记录错误总行数与 material 相关行数,或两数自相矛盾,或未逐行列出 material 相关错误)</fails_when>
<automated>node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md','utf8');const rows=t.split(/\r?\n/).filter(l=>/^\|\s*#04-06-A-\d+\s*\|/.test(l));if(rows.length<1){console.error('NO A ROWS');process.exit(1)}const bad=rows.filter(l=>((l.match(/[A-Za-z0-9_\-.\/]+\.tsx?:\d+/g)||[]).length<2));if(bad.length>0){console.error('A ROW MISSING TWO-SIDED ANCHOR '+bad.length);process.exit(1)}console.log('OK A rows '+rows.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "NO A ROWS" / "A ROW MISSING TWO-SIDED ANCHOR"(A 类未写端到端切片行,或该行缺命中处 / 基准侧锚点之一)</fails_when>
<automated>node -e "const fs=require('fs');const s=fs.readFileSync('.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md','utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf){console.error('NON-CRLF LINE ENDINGS '+(lf-crlf));process.exit(1)}console.log('OK CRLF lines '+crlf)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "NON-CRLF LINE ENDINGS"(交付文档出现非 CRLF 换行,违反 dev.md「换行使用 CRLF 格式」)</fails_when>
<automated>node -e "const fs=require('fs'),{execSync}=require('child_process');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md','utf8');const seg=(h)=>{const i=t.indexOf(h);if(i<0)return '';const rest=t.slice(i+h.length);const j=rest.search(/\r?\n## /);return j<0?rest:rest.slice(0,j)};const ms=seg('## \u65b9\u6cd5');if(ms.indexOf('\u53ea\u8bfb\u8d77\u59cb\u57fa\u7ebf')<0){console.error('READONLY BASELINE NOT RECORDED');process.exit(1)}const rec=(ms.match(/^[ MARCUD?!]{2} (?:packages|packages-user|src)[^\r\n]*/gm)||[]).map(s=>s.replace(/\s+$/,''));let raw='';try{raw=execSync('git status --porcelain -- packages packages-user src',{encoding:'utf8'})}catch(e){console.error('GIT STATUS FAILED '+(e.message||''));process.exit(1)}const out=raw.replace(/\s+$/,'');const cur=out.length>0?out.split(/\r?\n/).map(s=>s.replace(/\s+$/,'')):[];const extra=cur.filter(l=>rec.indexOf(l)<0);const gone=rec.filter(l=>cur.indexOf(l)<0);if(extra.length+gone.length>0){console.error('READONLY BASELINE CHANGED extra=['+extra.join(' ; ')+'] missing=['+gone.join(' ; ')+']');process.exit(1)}console.log('OK read-only baseline verbatim entries '+cur.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "READONLY BASELINE NOT RECORDED" / "READONLY BASELINE CHANGED"(`## 方法` 未把起始 `git status --porcelain -- packages packages-user src` 输出逐行原样记录,或记录内容与当前状态不一致 —— 违反只读约束)</fails_when>
</verify>
<acceptance_criteria>
- 交付文档存在于 `.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md`,11 个二级标题逐字存在(`## 背景` / `## 方法` / `## 基准变更(4e305e3)` / `## 类型门禁实测基线` / `## A 已移除符号的剩余引用` / `## B 改名的剩余引用` / `## C 新增与收紧约束的未实现` / `## D big-image 残留(实现与消费者)` / `## E 范围外命中(仅报告)` / `## F 未能从阅读确定(未猜测)` / `## 处置`)
- 元信息块 6 行齐备(日期 / 范围 / 性质 / 接口基准 / 基准提交 / 下一步),且「范围」明写限 `packages-user`、`packages/` 不纳入、`src/` 命中仅报告
- `## 方法` 含「只读起始基线」条目,**逐行原样**记录起始 `git status --porcelain -- packages packages-user src` 的输出(规划日为空 → 写「(无)」);门禁断言记录条目与当前状态逐条一致
- `## 方法` 写死 A/B/C/D/E/F 六类判定口径与 **A(消费者调用点)/ D(实现残留与 legacy 概念路径)分列规则**,并写明每行须含命中处与基准侧两个 `file:line` 锚点、未定项一律进 F 类
- `## 基准变更(4e305e3)` 覆盖 7 个已移除符号(含各自现行基准侧锚点)、3 个改名接口、`readonly textures: ITextureStore`、四处 `extends ICoreStateExtended`,并点名定义源 `packages-user/data-state/src/types.ts:45-49`;另登记同提交的相邻变更(`rafExcitation` / `excitationDivider` / `client-system`)
- `## 类型门禁实测基线` 逐字记录 `- **错误总行数**:<N>` 与 `- **material 相关错误行数**:<M>`(规划日 199 / 20,执行日以实测为准),逐行列出全部 material 相关错误,并单列与本改动无关的既有错误与该划分口径「须用户确认」的说明
- A 类含固定列序表头与至少 1 条 `#04-06-A-*` 行,该行含命中处与基准侧两个 `file:line` 锚点,且行内无修复建议
- 相对记录在 `## 方法` 的只读起始基线零变化(porcelain 条目逐条一致);未修改任何生产源码;未新增计划文件;全文 CRLF
</acceptance_criteria>
<done>交付文档骨架(11 节)+ `## 基准变更(4e305e3)` 三类差异 + `## 类型门禁实测基线` 实测数字 + A 类 `#04-06-A-01` 端到端双锚点切片全部落位;结构 / 基准关键词 / 类型基线 / A 行双锚点 / CRLF / 只读基线六道门禁转绿;Task 2 只需在同一模板上横向补齐</done>
</task>
<task type="auto">
<name>Task 2:A–F 六类横向补齐 —— 7 个已移除符号与 9 处调用点 / 三个改名的 0 命中机器证据 / 5 项约束未实现(带实测错误码)/ big-image 实现残留逐成员 / 范围外命中仅报告 / 未确定项只登记</name>
<files>.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md</files>
<read_first>
- .planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md(Task 1 的产出:在同一骨架上横向补齐,**不改结构、不改元信息块、不改已定稿的 `## 基准变更` 与 `## 类型门禁实测基线` 数字**)
- .planning/phases/04-render-adaptation/04-CONTEXT.md(D-30 / D-31 / D-32 原文;D-07 粒度;D-09 子代理;D-12 / D-18 / D-19 / D-22 / D-23 边界)
- packages-user/client-base/src/material/manager.ts:16-24,38-43,56,77,86,231-237,552-593(D 类逐成员:`IBigImageReturn` import `:20`、`bigImageStore` `:43`、`bigImageData` `:56`、`bigImageId` `:77`、`setDefaultFrame` 耦合 `:233-235`、`setBigImage` `:552-573`、`isBigImage` `:575-577`、`getBigImage` `:579-581`、`getBigImageByAlias` `:583-587`、`getIfBigImage` `:589-593`)
- packages-user/client-base/src/material/autotile.ts:42-48 与 packages-user/client-base/src/material/builder.ts:12-29,90-115(C 类:`AutotileProcessor` / `AssetBuilder` / `TrackedAssetData` 无 `state`;`AssetBuilder` 由 `manager.ts:86` 构造、`TrackedAssetData` 由 `builder.ts:28` 构造)
- packages-user/client-modules/src/client.ts:44-93(构造 / 接线面:`IClientCore` 的 `materials` / `autotile`、`:84` `new MaterialManager()`、`:85` `new AutotileProcessor(this.materials)`、`ClientCore extends CoreState`;`:90-93` 的 `fallbackLoad(this.materials)`)
- packages-user/client-modules/src/render/map/renderer.ts:213-240,649-657,1244-1252(`:217` `constructor(readonly manager: ITextureManager)`、`:234` `new AutotileProcessor(manager)`、`:652` `manager.bigImageStore.keys()`、`:653` `manager.getBigImage(identifier)`、`:1250` `manager.getIfBigImage(tile)`)
- packages-user/client-modules/src/render/map/extension/hero.ts:235-242,360-368,424-432(`getIfBigImage` 三处;只登记引用,不涉 `HeroRendering` 语义(D-18))
- packages-user/client-modules/src/render/map/extension/door.ts:30-56 与 packages-user/client-modules/src/render/map/vertex.ts:544-548(`getIfBigImage` 其余调用点)
- packages-user/client-modules/src/render/elements/cache.ts:25-48,90-95,144-160,190,215,243,276(D 类 legacy 概念路径:`RenderableDataBase.bigImage`、`core.material.images.images[bigImage]`、`enemys[id].bigImage`)
- packages-user/client-modules/src/fallback/load.ts:1-7,53-137(`ITextureManager` 消费者;只登记,不属残留)
- packages-user/client-base/src/load/loader.ts:1-60(`ITextureManager` 消费者;其 6 条类型错误**不属** material,须在类型基线中分列)
- packages-user/client-base/src/material/index.ts 与 packages-user/client-base/src/index.ts(barrel 导出核对)、packages-user/client-system/{package.json,src/index.ts,src/types.ts}(新包,须纳入命中面)
- packages-user/client-base/package.json(依赖声明面:是否声明 `@user/data-state`)
- src/types/declaration/{enemy.d.ts,core.d.ts,map.d.ts,status.d.ts}(E 类:`bigImage` 数据字段与 legacy API 声明,仅报告)
- dev.md(「项目结构」单向依赖;「双端分离」)
</read_first>
<action>
(1) **A 类补齐(已移除符号的引用点)**:按固定列序表逐条写满 —— ① `getIfBigImage`:`render/map/extension/door.ts:36,46`(`#04-06-A-01`,Task 1 已写)、`render/map/extension/hero.ts:239,364,428`、`render/map/renderer.ts:1250`、`render/map/vertex.ts:546`,共 **7 处调用点**;② `bigImageStore`:`render/map/renderer.ts:652`(`this.manager.bigImageStore.keys()`,用于 `getOffsetPool` 收集大图偏移);③ `getBigImage`:`render/map/renderer.ts:653`;④ 无调用点的 `setBigImage` / `isBigImage` / `getBigImageByAlias` **必须显式登记为「调用点 0 处」**并给出核对命令(`git grep -n -E "setBigImage|isBigImage|getBigImageByAlias" -- packages packages-user src` 仅命中 `client-base/src/material/manager.ts` 自身的定义行);⑤ `IBigImageReturn`:`client-base/src/material/manager.ts:20`(import 自 `./types`,该符号已不在基准导出中)与 `:556,:568`(实现内的类型标注,同时计入 D 类)。基准侧锚点统一引用 `client-base/src/material/types.ts` 上该符号原属接口的**现行**行(`ITextureGetter` `:196`、`ITextureAliasGetter` `:228`、`ITextureManager` `:260`)。**行内不得出现修复建议或替代写法**(替代是否成立属 F 类)。
(2) **B 类(改名的剩余引用)**:逐个列出 `IMaterialManager` / `IMaterialGetter` / `IMaterialAliasGetter`,每个给出「当日源码面命中数 = 0」的结论与原样记录的核对命令输出 `git grep -n -E "IMaterialManager|IMaterialGetter|IMaterialAliasGetter" -- packages packages-user src`(无输出即 0 命中);随后**单列** `.planning/graphs/graph.json` 与 `.planning/graphs/.last-build-snapshot.json` 中仍存在的 `IMaterialManager` / `IMaterialGetter` 等标签,写明其为**过期工装态**(快照时间戳 2026-09-07,早于 `4e305e3` 的 2026-09-21)、**不计入源码命中、不作为基准**,归 E 类登记。B 类节末写固定结论行 `- **结论**:三个旧接口名在源码面命中 0 处(仅工装态过期标签存在,见 E 类)。`
(3) **C 类(新增与收紧约束的未实现)**:逐条写 5 项,每条含基准侧锚点 + 实现侧锚点 + 当日实测错误码与实际错误原文摘要。① `ITextureManager.textures`(基准 `material/types.ts:262-263`)未实现于 `MaterialManager`(`material/manager.ts:38`,成员仅 `tileStore` `:39` / `tilesetStore` `:40` / `imageStore` `:41` / `assetStore` `:42` / `bigImageStore` `:43`),命中 `client-modules/src/client.ts:84` 实测 TS2739(`Type 'MaterialManager' is missing the following properties from type 'ITextureManager': textures, state`)。② `ICoreStateExtended`(定义源 `packages-user/data-state/src/types.ts:45-49`)经 `ITextureManager`(`material/types.ts:261`)→ `MaterialManager`(`manager.ts:38`)缺 `state`,实测 TS2420。③ 经 `IAutotileProcessor`(`material/types.ts:104`)→ `AutotileProcessor`(`autotile.ts:42`,构造器仅参数 `manager`)缺 `state`,实测 TS2420;构造点 `client-modules/src/client.ts:85` 与 `render/map/renderer.ts:234` 实测 TS2741。④ 经 `IAssetBuilder`(`material/types.ts:430`)→ `AssetBuilder`(`builder.ts:12`)缺 `state`,实测 TS2420;构造点 `manager.ts:86`(`new AssetBuilder(this)`)与 `builder.ts:91` 实测 TS2741。⑤ 经 `ITrackedAssetData`(`material/types.ts:462-463`)→ `TrackedAssetData`(`builder.ts:99`)缺 `state`,实测 TS2420。**登记构造 / 接线面**(不裁定):`manager.ts:86`、`builder.ts:28`、`client.ts:84`、`client.ts:85`、`render/map/renderer.ts:234`,并写明 `ClientCore extends CoreState`(`client.ts:45`)这一事实仅作背景,**来源与注入方式进 F 类**。
(4) **D 类(big-image 残留)逐成员**:按固定列序表逐条写 —— `bigImageStore`(`material/manager.ts:43`)、`bigImageData`(`:56`)、`bigImageId`(`:77`)、`setDefaultFrame` 对 `bigImageData` 的耦合(`:233-235`)、`setBigImage`(`:552-573` 含 `:557,:558,:567,:568-571`)、`isBigImage`(`:575-577`)、`getBigImage`(`:579-581`)、`getBigImageByAlias`(`:583-587`)、`getIfBigImage`(`:589-593`)、`IBigImageReturn` 类型引用(`:20,:556,:568`);以及 **legacy 概念路径** `client-modules/src/render/elements/cache.ts`(`:36` `RenderableDataBase.bigImage`、`:48` / `:144` / `:190` / `:215` / `:243` / `:276` 的赋值、`:153,:157` 取自 `enemys[id].bigImage`、`:159-160` 经 `core.material.images.images[bigImage]` 读旧全局)。每条给「是否仍属某个接口成员」的判定(`否`)与「是否导致类型错误」的判定(`manager.ts` 的成员导致 D 类与 C 类重叠的条目须互相引用;`cache.ts` 不导致 material 类型错误,因其走 `core.*` legacy 全局)。**A 类与 D 类对同一成员的两条记录必须互相引用**(例如 A 类的 `renderer.ts:652` ↔ D 类的 `manager.ts:43`)。
(5) **E 类(范围外命中·仅报告)**:逐条登记 `src/types/declaration/enemy.d.ts:65`(`bigImage?: ImageIds`)、`core.d.ts:1286`(同)、`map.d.ts:354`(`bigImage: HTMLImageElement`)与 `:1443`(`_getBigImageInfo`)、`status.d.ts:391`(`bigImage: HTMLImageElement`);`packages/` 的**零命中**结论与原样记录的核对命令 `git grep -n -i bigImage -- packages` 输出(无输出即 0 命中);`.planning/graphs/{graph.json,.last-build-snapshot.json}` 的过期标签;`packages-user/client-base/package.json` **未声明** `@user/data-state` 而 `material/types.ts:9` 与 `client-base/src/types.ts:3` 已 `import` 的事实(登记,是否算问题**交由用户判断**)。该节标题下明写 `**本节仅报告:不在本阶段修改,也不提出修改建议。**`,且全节**不得**出现「应改为」「建议」「修复」「删除」等修改性措辞针对 `src/` 与 `packages/`。
(6) **F 类(未能从阅读确定·未猜测)**:每条以 `- **#04-06-F-NN**:` 开头,写「现象 + 为什么读不出来 + 需要用户裁决的点」,至少覆盖:① `getIfBigImage` 的语义替代是否成立(新基准只提供 `getTile` / `getRenderable` / `getImage`;`door.ts` 的大图开门帧序、`hero.ts` 的贴图取值、`vertex.ts` 的顶点裁剪能否由 `getTile` 等价承接);② `ITextureManager.textures`(`material/types.ts:262`)应由哪个现有 store(`tileStore` 还是新建)承担;③ 四个实现类缺失的 `state` 从何处注入(`ClientCore extends CoreState`,构造点是否传 `this`);④ `cache.ts` 的 legacy bigImage 路径属本 material 增量还是 Phase 5 legacy 清退边界;⑤ `setDefaultFrame` 与 `bigImageData` 的耦合在新概念下是否有对位;⑥ `.planning/graphs/*` 是否需要重建。**不得**把本节任何一条写成错配、缺失或残留。
(7) **重型整文件读取可按 D-09 委派只读子代理**(`manager.ts`、`types.ts`、`client.ts`、`renderer.ts`、`hero.ts`、`cache.ts`):子代理只回传事实表(每行:成员名 + 所属 `file:line` + 形态或签名),**不得写文件、不得贴大段源码**;主执行者按 `file:line` 切片复核后落笔。
(8) **只修改该交付文档**;不得改动任何生产源码、不得新增计划文件、不得修改 Task 1 已定稿的数字与结构。
</action>
<verify>
<automated>node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md','utf8');const seg=(h)=>{const i=t.indexOf(h);if(i<0)return '';const rest=t.slice(i+h.length);const j=rest.search(/\r?\n## /);return j<0?rest:rest.slice(0,j)};const A=seg('## A \u5df2\u79fb\u9664\u7b26\u53f7');const B=seg('## B \u6539\u540d\u7684\u5269\u4f59\u5f15\u7528');const C=seg('## C \u65b0\u589e\u4e0e\u6536\u7d27\u7ea6\u675f');const D=seg('## D big-image');const E=seg('## E \u8303\u56f4\u5916\u547d\u4e2d');const F=seg('## F \u672a\u80fd\u4ece\u9605\u8bfb\u786e\u5b9a');const cnt=(s,re)=>(s.match(re)||[]).length;const rows=s=>cnt(s,/^\|\s*#04-06-[ABCDE]-\d+\s*\|/gm);const anch=l=>cnt(l,/[A-Za-z0-9_\-.\/]+\.tsx?:\d+/g);const problems=[];if(rows(A)<9)problems.push('A ROWS '+rows(A));if(rows(C)<5)problems.push('C ROWS '+rows(C));if(rows(D)<9)problems.push('D ROWS '+rows(D));if(rows(E)<5)problems.push('E ROWS '+rows(E));if(cnt(F,/^- \*\*#04-06-F-\d+/gm)<5)problems.push('F ITEMS '+cnt(F,/^- \*\*#04-06-F-\d+/gm));for(const pair of [['A',A],['C',C],['D',D],['E',E]]){const bad=pair[1].split(/\r?\n/).filter(l=>/^\|\s*#04-06-[ABCDE]-\d+\s*\|/.test(l)).filter(l=>anch(l)<2);if(bad.length>0)problems.push(pair[0]+' ROWS WITHOUT TWO-SIDED ANCHOR '+bad.length)}const rm=['IBigImageReturn','isBigImage','getBigImage','getIfBigImage','getBigImageByAlias','setBigImage','bigImageStore'];const missA=rm.filter(k=>A.indexOf(k)<0);if(missA.length>0)problems.push('A MISSING SYMBOLS '+missA.join('|'));const missB=['IMaterialManager','IMaterialGetter','IMaterialAliasGetter'].filter(k=>B.indexOf(k)<0);if(missB.length>0)problems.push('B MISSING OLD NAMES '+missB.join('|'));const missC=['textures','MaterialManager','AutotileProcessor','AssetBuilder','TrackedAssetData','ICoreStateExtended'].filter(k=>C.indexOf(k)<0);if(missC.length>0)problems.push('C MISSING '+missC.join('|'));const missD=['bigImageStore','bigImageData','bigImageId','setBigImage','isBigImage','getBigImageByAlias','IBigImageReturn','cache.ts'].filter(k=>D.indexOf(k)<0);if(missD.length>0)problems.push('D MISSING '+missD.join('|'));if(E.indexOf('src/types/declaration')<0)problems.push('E MISSING SRC DECL');if(E.indexOf('.planning/graphs')<0)problems.push('E MISSING GRAPH ARTIFACT');if(problems.length>0){console.error('CATEGORY GATE FAILED '+problems.join(' ; '));process.exit(1)}console.log('OK A '+rows(A)+' C '+rows(C)+' D '+rows(D)+' E '+rows(E)+' F '+cnt(F,/^- \*\*#04-06-F-\d+/gm))"</automated>
<fails_when>non-zero exit, 或 stderr 含 "CATEGORY GATE FAILED"(A/C/D/E 任一类行数低于下限、任一行缺命中处或基准侧锚点、A 未覆盖 7 个已移除符号、B 未列三个旧名、C 未覆盖 `textures` 与四个实现类、D 未覆盖关键残留成员或 `cache.ts`、E 未覆盖 `src/types/declaration` 与 `.planning/graphs`、F 少于 5 条未确定项)</fails_when>
<automated>node -e "const{execSync}=require('child_process');let o='',st=0;try{o=execSync('git grep -n -E \"IMaterialManager|IMaterialGetter|IMaterialAliasGetter\" -- packages packages-user src',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'');st=e.status}if(o.trim().length>0){console.error('STALE MATERIAL NAMES STILL IN SOURCE: '+o.trim().split(/\r?\n/).slice(0,5).join(' | '));process.exit(1)}if(st!==0&&st!==1){console.error('GIT GREP ERROR status '+st);process.exit(1)}console.log('OK zero stale material names in source (git grep status '+st+')')"</automated>
<fails_when>non-zero exit, 或 stderr 含 "STALE MATERIAL NAMES STILL IN SOURCE" / "GIT GREP ERROR"(源码面仍存在 `IMaterialManager` / `IMaterialGetter` / `IMaterialAliasGetter`,即 B 类的「0 命中」结论为臆造)</fails_when>
<automated>node -e "const fs=require('fs');const s=fs.readFileSync('.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md','utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf){console.error('NON-CRLF LINE ENDINGS '+(lf-crlf));process.exit(1)}console.log('OK CRLF lines '+crlf)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "NON-CRLF LINE ENDINGS"(追加内容未保持 CRLF)</fails_when>
</verify>
<acceptance_criteria>
- A 类 **≥ 9 行**,覆盖 7 个已移除符号;`getIfBigImage` 的 7 处调用点(`door.ts:36,46`;`hero.ts:239,364,428`;`renderer.ts:1250`;`vertex.ts:546`)、`bigImageStore`(`renderer.ts:652`)、`getBigImage`(`renderer.ts:653`)逐条落位;`setBigImage` / `isBigImage` / `getBigImageByAlias` 显式登记为「调用点 0 处」并附核对命令;`IBigImageReturn` 的引用侧(`manager.ts:20`)落位;行内无修复建议
- B 类逐个给出三个旧名「源码面命中 0 处」的结论与原样记录的核对命令输出;单列 `.planning/graphs/*` 的过期标签并声明其不计入源码命中;含固定结论行
- C 类 **≥ 5 行**,覆盖 `textures` 未实现与四个实现类缺 `state`;每行含基准侧锚点 + 实现侧锚点 + 当日实测错误码与错误原文摘要;登记构造 / 接线面(`manager.ts:86`、`builder.ts:28`、`client.ts:84`、`client.ts:85`、`render/map/renderer.ts:234`)并不裁定 `state` 来源
- D 类 **≥ 9 行**,逐成员覆盖 `bigImageStore` / `bigImageData` / `bigImageId` / `setDefaultFrame` 耦合 / `setBigImage` / `isBigImage` / `getBigImage` / `getBigImageByAlias` / `getIfBigImage` / `IBigImageReturn` 与 `cache.ts` 的 legacy 概念路径;A 与 D 对同一成员的两条记录互相引用;`cache.ts` 明确标注「不走 `ITextureManager`、不导致 material 类型错误」
- E 类 **≥ 5 行**,覆盖 5 处 `src/types/declaration/*` 命中、`packages/` 零命中(附命令与结论)、`.planning/graphs/*` 过期标签、`client-base/package.json` 未声明 `@user/data-state` 的事实;节内明写「仅报告,不修改、不提建议」,且针对 `src/` 与 `packages/` 无修改性措辞
- F 类 **≥ 5 条** `#04-06-F-*` 未确定项,覆盖 6 个既定方向,且均未写成错配 / 缺失 / 残留
- 所有 A–E 行至少含命中处与基准侧两个 `file:line` 锚点;相对记录在 `## 方法` 的只读起始基线零变化;未修改任何生产源码;全文 CRLF
</acceptance_criteria>
<done>A–F 六类横向补齐:7 个已移除符号与 9 处调用点、三个改名的 0 命中机器证据、5 项约束未实现(带实测错误码与构造面)、big-image 实现残留逐成员、范围外命中仅报告、未确定项只登记;类别行数 / 双锚点 / 关键词 / B 类 0 命中 / CRLF 门禁转绿</done>
</task>
<task type="auto">
<name>Task 3:`## 处置` 定稿 + 只读/类型/类别/既有计划保护/计划范围/CRLF 六道门禁 + 人工复核 + `04-06-SUMMARY.md` 落盘与提交</name>
<files>.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md, .planning/phases/04-render-adaptation/04-06-SUMMARY.md</files>
<read_first>
- .planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md(Task 1/2 产出,做定稿而非重写)
- .planning/phases/04-render-adaptation/04-CONTEXT.md(D-30 / D-31 / D-32;Deferred Ideas)
- .planning/phases/04-render-adaptation/04-05-SUMMARY.md:39-60(既有 SUMMARY 的元信息块与「执行前核对 / 门禁」写法,可对齐版式)
- .planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md:128-135(既有清点文档的 `## 处置` 写法)
- .planning/phases/04-render-adaptation/04-06-PLAN.md(本计划:门禁与禁令)
- dev.md(「其他要求」的 CRLF 要求;「双端分离」)
</read_first>
<action>
(1) **`## 处置` 定稿**:明写 —— 本文件是 material 接口适应(D-30 / D-31)**第一步(只读清点)**的交付物;本步**未修改任何生产代码**、`files_modified` 仅本文件;相对 `## 方法` 记录的只读起始基线零变化;**第二步(material 接口适应实施)未规划**,待用户审阅本文件后另行规划(D-32);范围限 `packages-user`,`packages/` 不纳入、`src/` 命中仅报告(D-32);A / B / C / D / E / F 六类中只有 A / C / D 是 `packages-user` 内的待处置项,B 类已清零、E 类仅报告、F 类须用户裁决;`state` 的注入方式、`textures` 的承担者、`getIfBigImage` 的替代一律由用户裁决(不得由 AI 裁定,D-23 亦约束新增代码不得取全局单例);`core.*` legacy 适配(D-12)与移动控制(D-19)、跟随者(D-22)、`HeroRendering` 语义(D-18)不属本步;`REND-01` / `REND-02` 保持 Pending;若当日实测数值与规划日(199 / 20)不同,在本节说明差异与原因。
(2) **运行六道门禁并按实测结果如实记录**:① 只读门禁(`git status --porcelain -- packages packages-user src` 与 `## 方法` 记录逐条一致);② 类型门禁一致性(重跑 `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||'')}console.log((o.match(/error TS\d+/g)||[]).length)"`,输出必须等于文档记录的**错误总行数**);③ 类别门禁(A/C/D/E 行数下限、每行双锚点、A 覆盖 7 符号、B 三个旧名、C 五个关键词、D 关键残留与 `cache.ts`、E 的 `src/types/declaration` 与 `.planning/graphs`、F ≥ 5 条);④ B 类 0 命中复验(`git grep` 源码面仍为 0);⑤ 既有计划保护 + 计划范围门禁(`04-05-PLAN.md` / `04-05-SUMMARY.md` 的 porcelain 为空;phase 目录内 `-PLAN.md` 仅允许 `04-01..04-06`);⑥ CRLF 门禁。任一门禁失败即停下修正文档后重跑,**不得**改门禁口径或伪造通过。
(3) **人工代码复核(不可自动化,如实记录结论)**:抽查 A 类中 `hero.ts` 的三处与 `vertex.ts` 的一处命中、D 类中 `manager.ts:552-593` 的五个方法与 `cache.ts:159-160` 的 legacy 读取,逐条打开当日源码确认「命中处锚点指向的确实是该成员的引用」「基准侧锚点指向的确实是该接口的现行行」,并把复核结论(通过 / 发现偏差及修正)写入 SUMMARY。发现偏差即回到文档修正。
(4) **写 `04-06-SUMMARY.md`**:按执行日实际结果写(元信息块:phase / plan / subsystem / tags / requires / provides / affects / tech-stack / key-files / key-decisions / actuals / metrics),并逐条记录:本 run 的实际产出(唯一交付文档 + 本 PLAN + 本 SUMMARY)、A–F 六类的实际行数与 7 个已移除符号 / 3 个改名 / 5 项约束的核对结论、`git status --porcelain -- packages packages-user src` 与 `pnpm exec vue-tsc --noEmit` 的当日实测值(与规划日差异须说明)、六道门禁的实际结果、人工复核结论、与规划日数值的差异说明、以及 `REND-01` / `REND-02` 仍为 Pending 的声明与「第二步未规划」。**不得**把未运行的检查写成通过。
(5) **提交**:以 `docs(04-06): material 接口影响范围清点(只读,D-30/D-31/D-32)` 提交**仅** `.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md`、`.planning/phases/04-render-adaptation/04-06-PLAN.md` 与 `.planning/phases/04-render-adaptation/04-06-SUMMARY.md` 三个文件;**不得**把任何生产源码改动或用户既有的未提交改动纳入本次提交(尤其不得提交 `.planning/phases/04-render-adaptation/04-CONTEXT.md` 的既有未提交改动),不得改写 / 覆盖 / 改号 `04-05-PLAN.md` 与 `04-05-SUMMARY.md`。
</action>
<verify>
<automated>node -e "const fs=require('fs'),{execSync}=require('child_process');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md','utf8');const seg=(h)=>{const i=t.indexOf(h);if(i<0)return '';const rest=t.slice(i+h.length);const j=rest.search(/\r?\n## /);return j<0?rest:rest.slice(0,j)};const ms=seg('## \u65b9\u6cd5');if(ms.indexOf('\u53ea\u8bfb\u8d77\u59cb\u57fa\u7ebf')<0){console.error('READONLY BASELINE NOT RECORDED');process.exit(1)}const rec=(ms.match(/^[ MARCUD?!]{2} (?:packages|packages-user|src)[^\r\n]*/gm)||[]).map(s=>s.replace(/\s+$/,''));let raw='';try{raw=execSync('git status --porcelain -- packages packages-user src',{encoding:'utf8'})}catch(e){console.error('GIT STATUS FAILED '+(e.message||''));process.exit(1)}const out=raw.replace(/\s+$/,'');const cur=out.length>0?out.split(/\r?\n/).map(s=>s.replace(/\s+$/,'')):[];const extra=cur.filter(l=>rec.indexOf(l)<0);const gone=rec.filter(l=>cur.indexOf(l)<0);if(extra.length+gone.length>0){console.error('READONLY BASELINE CHANGED extra=['+extra.join(' ; ')+'] missing=['+gone.join(' ; ')+']');process.exit(1)}console.log('OK read-only unchanged entries '+cur.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "READONLY BASELINE NOT RECORDED" / "READONLY BASELINE CHANGED"(`packages` / `packages-user` / `src` 下出现了新增 / 消失 / 状态码变化的 worktree 条目 —— 违反只读约束;用户既有改动不得回滚 / 暂存 / 提交)</fails_when>
<automated>node -e "const fs=require('fs'),{execSync}=require('child_process');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md','utf8');const a=t.match(/\u9519\u8bef\u603b\u884c\u6570\uff1a\s*(\d+)/);const b=t.match(/material \u76f8\u5173\u9519\u8bef\u884c\u6570\uff1a\s*(\d+)/);if(!a||!b){console.error('MISSING TSC BASELINE FIGURES');process.exit(1)}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 n=(o.match(/error TS\d+/g)||[]).length;if(String(n)!==a[1]){console.error('TSC TOTAL CHANGED '+n+' vs '+a[1]);process.exit(1)}const api=/client-(base|modules)\//;const hits=(o.split(/\r?\n/).filter(l=>api.test(l)&&/\u2014|error TS/.test(l))).length;console.log('OK tsc total unchanged '+n+' material '+b[1]+' clientErrorLines '+hits)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "MISSING TSC BASELINE FIGURES" / "TSC TOTAL CHANGED"(类型错误总行数与文档记录不一致 —— 生产源码被改动或基线未如实记录;后者使只读证明失效)</fails_when>
<automated>node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md','utf8');const seg=(h)=>{const i=t.indexOf(h);if(i<0)return '';const rest=t.slice(i+h.length);const j=rest.search(/\r?\n## /);return j<0?rest:rest.slice(0,j)};const A=seg('## A \u5df2\u79fb\u9664\u7b26\u53f7');const B=seg('## B \u6539\u540d\u7684\u5269\u4f59\u5f15\u7528');const C=seg('## C \u65b0\u589e\u4e0e\u6536\u7d27\u7ea6\u675f');const D=seg('## D big-image');const E=seg('## E \u8303\u56f4\u5916\u547d\u4e2d');const F=seg('## F \u672a\u80fd\u4ece\u9605\u8bfb\u786e\u5b9a');const cnt=(s,re)=>(s.match(re)||[]).length;const rows=s=>cnt(s,/^\|\s*#04-06-[ABCDE]-\d+\s*\|/gm);const anch=l=>cnt(l,/[A-Za-z0-9_\-.\/]+\.tsx?:\d+/g);const problems=[];if(rows(A)<9)problems.push('A ROWS '+rows(A));if(rows(C)<5)problems.push('C ROWS '+rows(C));if(rows(D)<9)problems.push('D ROWS '+rows(D));if(rows(E)<5)problems.push('E ROWS '+rows(E));if(cnt(F,/^- \*\*#04-06-F-\d+/gm)<5)problems.push('F ITEMS '+cnt(F,/^- \*\*#04-06-F-\d+/gm));for(const pair of [['A',A],['C',C],['D',D],['E',E]]){const bad=pair[1].split(/\r?\n/).filter(l=>/^\|\s*#04-06-[ABCDE]-\d+\s*\|/.test(l)).filter(l=>anch(l)<2);if(bad.length>0)problems.push(pair[0]+' ROWS WITHOUT TWO-SIDED ANCHOR '+bad.length)}const rm=['IBigImageReturn','isBigImage','getBigImage','getIfBigImage','getBigImageByAlias','setBigImage','bigImageStore'];const missA=rm.filter(k=>A.indexOf(k)<0);if(missA.length>0)problems.push('A MISSING SYMBOLS '+missA.join('|'));const missB=['IMaterialManager','IMaterialGetter','IMaterialAliasGetter'].filter(k=>B.indexOf(k)<0);if(missB.length>0)problems.push('B MISSING OLD NAMES '+missB.join('|'));const missC=['textures','MaterialManager','AutotileProcessor','AssetBuilder','TrackedAssetData','ICoreStateExtended'].filter(k=>C.indexOf(k)<0);if(missC.length>0)problems.push('C MISSING '+missC.join('|'));const missD=['bigImageStore','bigImageData','bigImageId','setBigImage','isBigImage','getBigImageByAlias','IBigImageReturn','cache.ts'].filter(k=>D.indexOf(k)<0);if(missD.length>0)problems.push('D MISSING '+missD.join('|'));if(E.indexOf('src/types/declaration')<0)problems.push('E MISSING SRC DECL');if(E.indexOf('.planning/graphs')<0)problems.push('E MISSING GRAPH ARTIFACT');if(t.indexOf('\u5904\u7f6e')<0)problems.push('MISSING DISPOSITION SECTION');if(problems.length>0){console.error('CATEGORY GATE FAILED '+problems.join(' ; '));process.exit(1)}console.log('OK A '+rows(A)+' C '+rows(C)+' D '+rows(D)+' E '+rows(E)+' F '+cnt(F,/^- \*\*#04-06-F-\d+/gm))"</automated>
<fails_when>non-zero exit, 或 stderr 含 "CATEGORY GATE FAILED"(A/C/D/E 行数低于下限、任一行缺命中处或基准侧锚点、A 未覆盖 7 个已移除符号、B 未列三个旧名、C 未覆盖 `textures` 与四个实现类、D 未覆盖关键残留成员或 `cache.ts`、E 未覆盖 `src/types/declaration` 与 `.planning/graphs`、F 少于 5 条、缺 `## 处置`)</fails_when>
<automated>node -e "const{execSync}=require('child_process');let o='',st=0;try{o=execSync('git grep -n -E \"IMaterialManager|IMaterialGetter|IMaterialAliasGetter\" -- packages packages-user src',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'');st=e.status}if(o.trim().length>0){console.error('STALE MATERIAL NAMES STILL IN SOURCE: '+o.trim().split(/\r?\n/).slice(0,5).join(' | '));process.exit(1)}if(st!==0&&st!==1){console.error('GIT GREP ERROR status '+st);process.exit(1)}console.log('OK zero stale material names in source (git grep status '+st+')')"</automated>
<fails_when>non-zero exit, 或 stderr 含 "STALE MATERIAL NAMES STILL IN SOURCE" / "GIT GREP ERROR"(B 类「0 命中」结论为臆造)</fails_when>
<automated>node -e "const fs=require('fs'),{execSync}=require('child_process');const dir='.planning/phases/04-render-adaptation';const o=execSync('git status --porcelain -- .planning/phases/04-render-adaptation/04-05-PLAN.md .planning/phases/04-render-adaptation/04-05-SUMMARY.md',{encoding:'utf8'}).replace(/\s+$/,'');if(o.length>0){console.error('EXISTING 04-05 ARTIFACTS MODIFIED: '+o);process.exit(1)}const plans=fs.readdirSync(dir).filter(f=>/-PLAN\.md$/.test(f)).sort();const extra=plans.filter(p=>!/^04-0[1-6]-PLAN\.md$/.test(p));if(extra.length>0){console.error('UNEXPECTED PLAN FILES '+extra.join(','));process.exit(1)}if(!/04-06-SUMMARY\.md/.test(fs.readdirSync(dir).join(','))){console.error('MISSING 04-06 SUMMARY');process.exit(1)}console.log('OK 04-05 untouched; plans '+plans.join(','))"</automated>
<fails_when>non-zero exit, 或 stderr 含 "EXISTING 04-05 ARTIFACTS MODIFIED" / "UNEXPECTED PLAN FILES" / "MISSING 04-06 SUMMARY"(既有第五个增量产物被改写,或出现了未授权的 `04-07+` 计划文件——即提前规划了第二步,或未落盘 SUMMARY)</fails_when>
<automated>node -e "const fs=require('fs');const s=fs.readFileSync('.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md','utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf){console.error('NON-CRLF LINE ENDINGS '+(lf-crlf));process.exit(1)}console.log('OK CRLF lines '+crlf)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "NON-CRLF LINE ENDINGS"(交付文档出现非 CRLF 换行)</fails_when>
</verify>
<acceptance_criteria>
- `## 处置` 定稿,明写:本步只读、生产代码零改动、唯一交付物、相对只读起始基线零变化、**第二步未规划**(D-32)、范围限 `packages-user`(`packages/` 不纳入、`src/` 仅报告)、`state` / `textures` / `getIfBigImage` 替代由用户裁决、D-12 / D-18 / D-19 / D-22 边界、`REND-01` / `REND-02` 保持 Pending,并(如与规划日不同)说明实测差异
- 六道门禁全部转绿:只读门禁(porcelain 逐条一致)、类型门禁一致性(错误总行数等于记录值)、类别门禁(A ≥ 9 / C ≥ 5 / D ≥ 9 / E ≥ 5 / F ≥ 5 且每行双锚点与关键词齐备)、B 类 0 命中复验、既有计划保护 + 计划范围门禁(`04-05-*` 未被改写、无 `04-07+`、SUMMARY 已落盘)、CRLF 门禁
- 人工代码复核结论已记录(抽查 A 类 4 处命中与 D 类 `manager.ts:552-593` / `cache.ts:159-160`),偏差均已修正
- `04-06-SUMMARY.md` 已产出,如实记录当日实测值(porcelain / 类型错误数)、A–F 实际行数、六道门禁结果、人工复核结论、与规划日的差异说明、`REND-01` / `REND-02` 仍 Pending 与「第二步未规划」
- 提交仅含 `04-MATERIAL-INTERFACE-IMPACT.md`、`04-06-PLAN.md`、`04-06-SUMMARY.md` 三个文件;未纳入任何生产源码改动,未提交 `04-CONTEXT.md` 的既有未提交改动,未改写 `04-05-*`
</acceptance_criteria>
<done>material 接口影响台账定稿并交付:六道门禁全绿、既有 04-05 产物零改写、`04-06-SUMMARY.md` 落盘并提交;用户可据此审阅 A / C / D 三类待处置项与 F 类待裁决项,并规划第二步 material 接口适应</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| 生产源码 → 清点文档 | 只读引用 `packages-user` / `packages` / `src` 的代码与行号;不得产生任何写回(`packages` / `packages-user` / `src` 的 porcelain 必须零变化) |
| 类型检查结果 → 清点文档 | `pnpm exec vue-tsc --noEmit` 的输出被用作只读证据;记录值与重跑值必须一致,否则只读证明失效 |
| 基准(`material/types.ts` / `types.ts`)→ A–E 分类 | 基准决定「什么算已移除 / 已改名 / 新约束」;基准读错会整类错分 |
| 实现残留 → A / C / D 分类 | 把 `client-base` 内的实现残留(D)误报成接口缺失或消费者错配,会诱导错误的数据端/接口改动 |
| 执行者 / 子代理 → 执行前置关卡 | AGENTS.md 要求第一个 block 点先汇报并等待用户回复「可以执行」;`yolo` / `auto_advance` 或子代理派发都不是放行理由(Task 0 为 `blocking-human`、`autonomous: false`) |
| 既有 04-05 产物 → 本次编号 | phase 目录内已存在已执行的 `04-05-PLAN.md` / `04-05-SUMMARY.md`;改号/覆盖会破坏既有执行记录 |
| 清点范围 → Phase 5 / 后续增量 | 把 `src/` / `packages/` 命中或 `core.*` legacy 清退写成本阶段待办会扩大范围(D-12 / D-32) |
| 清点文档 → 第二步规划 | 第二步计划由用户审阅本文件后另行规划(D-32);文档若夹带修复方案即越权替用户设计 |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|-----------|----------|-----------|----------|-------------|-----------------|
| T-04-06-01 | Tampering | 生产源码 `packages-user` / `packages` / `src` | high | mitigate | Task 1 步骤 (0a) 记录 `git status --porcelain -- packages packages-user src` 的逐行基线(规划日为空);Task 1 / Task 3 门禁断言其后逐条一致;Task 3 另以 `pnpm exec vue-tsc --noEmit` 错误总行数等于记录值作为第二重只读证明;`files_modified` 仅含交付文档;prohibitions 明列生产文件零改动、不得触碰用户 committed 接口 |
| T-04-06-02 | Elevation of Privilege | 执行前置关卡(AGENTS.md 要求的第一处 block 点) | high | mitigate | Task 0 为 `checkpoint:decision` + `gate="blocking-human"` 且本计划 `autonomous: false`;Task 0 的 action ⑨/⑩ 与 acceptance_criteria 明示未收到用户「可以执行」四个字即必须停止、不派发执行子代理;`yolo` / `workflow.auto_advance` 不得自动放行 |
| T-04-06-03 | Tampering | 既有第五个增量产物 `04-05-PLAN.md` / `04-05-SUMMARY.md` | high | mitigate | 本计划只写 `04-06-*`;Task 3 门禁断言这两个文件的 porcelain 为空(未被改写);prohibitions 明列不得改写 / 覆盖 / 改号;Task 0 汇报编号冲突并请用户确认 |
| T-04-06-04 | Spoofing | A / B / C / D / E 分类 | high | mitigate | 分列规则写死(A = 消费者调用点、D = `client-base` 实现残留与 legacy 概念路径、C = 约束未实现、E = 范围外仅报告、F = 未确定);每行强制两侧 `file:line` 锚点;C 类必须带当日实测错误码与错误原文摘要;门禁机器判定行数下限、关键词与双锚点;F 类承接一切未定项 |
| T-04-06-05 | Spoofing | B 类「0 命中」结论 | high | mitigate | 不以文档自述为准:Task 2 / Task 3 门禁直接运行 `git grep -n -E "IMaterialManager|IMaterialGetter|IMaterialAliasGetter" -- packages packages-user src`,非零退出即失败;`.planning/graphs/*` 的过期标签单列为工装态并声明不计入源码命中 |
| T-04-06-06 | Repudiation | A–E 各行条目不可追溯 | medium | mitigate | 固定列序(`ID | 接口/成员名 | 命中处 | 基准侧锚点 | 问题描述 | 影响 | 置信`)+ 每行 ≥2 个 `file:line` 锚点 + 行数下限 + 关键词齐备,由 Task 2 / Task 3 门禁机器判定 |
| T-04-06-07 | Elevation of Privilege | 清点范围(`src/` / `packages/` / `core.*` legacy 清退 / 第二步实施) | medium | mitigate | D-12 / D-32 为显式边界:E 类标题下明写「仅报告,不修改、不提建议」;prohibitions 禁止为 `src/` / `packages/` 与 `cache.ts` 提修改建议;Task 3 门禁禁止出现 `04-07+` 计划文件与 `04-05` 改写 |
| T-04-06-08 | Denial of Service | 只读子代理越权写入(D-09) | low | mitigate | Task 2 步骤 (7) 明确子代理仅回传事实表、不得写文件、不得贴大段源码;最终由主执行者以只读门禁 + 类型门禁一致统一验证 |
| T-04-06-09 | Information Disclosure | 清点文档引用的源码行与接口名 | low | accept | 记录只含接口名、文件行号、成员签名与编译错误摘要,不含凭据 / 密钥 / 个人数据 |
| T-04-06-SC | Tampering | npm / pip / cargo 安装面 | high | mitigate | 本计划不新增依赖、不安装任何包(prohibitions);若出现安装需求即暂停并告知用户;门禁以 porcelain 基线覆盖 `package.json` / `pnpm-lock.yaml`(本计划零改动),无需包合法性核对 |
</threat_model>
<verification>
- **只读门禁**:`node -e "const{execSync}=require('child_process');console.log(JSON.stringify(execSync('git status --porcelain -- packages packages-user src',{encoding:'utf8'})))"` 的输出与交付文档 `## 方法` 的「只读起始基线」条目逐条一致(不新增、不消失、状态码不变)。
- **类型门禁一致性**:`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||'')}console.log((o.match(/error TS\d+/g)||[]).length)"` 的输出等于文档记录的「错误总行数」(规划日 199),且「material 相关错误行数」(规划日 20)在文档中逐行有对应的数据行。
- **类别门禁**:交付文档的 A ≥ 9 行 / C ≥ 5 行 / D ≥ 9 行 / E ≥ 5 行 / F ≥ 5 条,A–E 每行 ≥2 个 `file:line` 锚点,A 覆盖全部 7 个已移除符号,B 覆盖三个旧接口名,C 覆盖 `textures` 与四个实现类,D 覆盖关键残留成员与 `cache.ts`,E 覆盖 `src/types/declaration` 与 `.planning/graphs`。
- **B 类 0 命中复验**:`git grep -n -E "IMaterialManager|IMaterialGetter|IMaterialAliasGetter" -- packages packages-user src` 无输出。
- **既有计划保护 + 计划范围门禁**:`git status --porcelain -- .planning/phases/04-render-adaptation/04-05-PLAN.md .planning/phases/04-render-adaptation/04-05-SUMMARY.md` 为空;phase 目录内 `-PLAN.md` 仅 `04-01..04-06`;`04-06-SUMMARY.md` 已存在。
- **CRLF 门禁**:交付文档的 `\n` 总数等于 `\r\n` 总数(无孤立 LF)。
- **人工复核**:抽查 A 类 4 处命中与 D 类 `manager.ts:552-593`、`cache.ts:159-160` 的锚点指向,结论与偏差修正记录在 `04-06-SUMMARY.md`。
- **不可自动化项的诚实说明**:本步为只读清点,无运行时行为可观察;除上述命令外不存在可运行门禁,不得以任何无法运行的门禁冒充通过。
</verification>
<output>
Create `.planning/phases/04-render-adaptation/04-06-SUMMARY.md` when done
</output>
## Artifacts this phase produces
| Artifact | Kind | Purpose |
|----------|------|---------|
| `.planning/phases/04-render-adaptation/04-MATERIAL-INTERFACE-IMPACT.md` | 交付文档(**唯一** `files_modified`) | material 接口(`4e305e3` 基准)在 `packages-user` 全量的只读影响台账:元信息块 + 背景 / 方法 / 基准变更(4e305e3)/ 类型门禁实测基线 / A 已移除符号的剩余引用 / B 改名的剩余引用 / C 新增与收紧约束的未实现 / D big-image 残留(实现与消费者)/ E 范围外命中(仅报告)/ F 未能从阅读确定(未猜测)/ 处置 |
| `.planning/phases/04-render-adaptation/04-06-PLAN.md` | 计划簿记 | 本计划(编号 04-06;`04-05` 已被既有已执行增量占用) |
| `.planning/phases/04-render-adaptation/04-06-SUMMARY.md` | 执行簿记 | 当日实测值(porcelain / `pnpm exec vue-tsc --noEmit` 错误数)、A–F 实际行数、六道门禁结果、人工复核结论、与规划日的差异说明、`REND-01` / `REND-02` 仍 Pending 与「第二步未规划」 |
| 门禁结果(会话内可复跑) | 验证证据 | 只读门禁(porcelain 逐条一致)、类型门禁一致性(错误总行数等于记录值)、类别门禁(A ≥ 9 / C ≥ 5 / D ≥ 9 / E ≥ 5 / F ≥ 5 与双锚点 / 关键词)、B 类 0 命中复验、既有计划保护 + 计划范围门禁(`04-05-*` 未改写、无 `04-07+`、SUMMARY 已落盘)、CRLF 门禁 |
| 显式**不**产出 | 边界声明 | 任何生产代码改动、任何测试文件、任何依赖变更、任何修复方案或第二步实施计划(D-32:第二步待用户审阅本文件后另行规划) |