diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index ac481bd..fc29afe 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -181,9 +181,10 @@ Plans:
3. 移动端(窄屏)布局下,同一场景正常显示且可操作
4. 数据端与渲染端保持双端分离——数据端无 DOM,仍可在 Node 环境跑回放验证
-**Plans**: 9 已执行 + 1 已规划待执行(04-10) — 04-01..04-07 已执行(04-06 = material 接口影响范围清点·只读;04-07 = material 接口适应实施·第二步);04-08(TextureManager 新接口影响范围清点·只读,D-38/D-39/D-40/D-41/D-42 第一步)已执行;04-09(TextureManager 新接口适应实施·第二步·代码改动,D-43..D-50)已执行;**04-10(渲染端结构性重构影响范围清点·只读,D-51..D-57 第一步)已规划待执行**(执行前须 Task 0 汇报关卡等待用户「可以执行」);渲染适配与移动端/桌面端双布局(REND-01/REND-02)整体仍未完成
+**Plans**: 10 已执行 + 1 已规划待执行(04-11) — 04-01..04-07 已执行(04-06 = material 接口影响范围清点·只读;04-07 = material 接口适应实施·第二步);04-08(TextureManager 新接口影响范围清点·只读,D-38/D-39/D-40/D-41/D-42 第一步)已执行;04-09(TextureManager 新接口适应实施·第二步·代码改动,D-43..D-50)已执行;04-10(渲染端结构性重构影响范围清点·只读,D-51..D-57 第一步)已执行;**04-11(渲染端结构性重构·第二步·移植实施·代码改动,D-58..D-68)已规划待执行**(执行前须 Task 0 汇报关卡等待用户「可以执行」);渲染适配与移动端/桌面端双布局(REND-01/REND-02)整体仍未完成
**Plan register note (2026-09-21):** 第五个增量 `04-05`(收口 `#04-01-M-06` / `#04-01-M-09`:状态栏 9 个数值属性改经 `client.hero.attribute.getFinalAttribute(...)` 读取 + 逐图层钩子类 `RendererLayerHook`,2026-09-20 执行)此前未登记进本路线图,本次一并补登;第六个增量 `04-06` = material 接口适应(D-30/D-31/D-32)**第一步·只读影响清点**,产出 `04-MATERIAL-INTERFACE-IMPACT.md`,第二步(实施)待用户审阅后另行规划。第七个增量 `04-07` = material 接口适应实施(**第二步·代码改动**,D-33..D-37),2026-09-22 已执行(10 文件原子提交)。**第八个增量 `04-08` = TextureManager 新接口适应(D-38..D-42)第一步·只读影响清点**,产出 `04-TEXTURE-INTERFACE-IMPACT.md`;第二步(实施)待用户审阅后另行规划。
**Plan register note (2026-09-24):** 第十个增量 `04-10` = 渲染端结构性重构(用户裁定 2026-09-23 · D-51..D-57)**第一步·只读影响清点**:把 `packages-user/client-modules/src/render/` 下的通用内容(`render/components`、`render/elements`、`render/map` 三文件夹 + `render/utils/layout.ts`,规划日实测 **39** 文件)迁往渲染系统层 `packages-user/client-base/src/`(D-51/D-52;目标布局按 D-53:`client-base/src/{components,elements,map}/` + `client-base/src/layout/`,**不新建 `render/` 目录**);不移植 `render/utils/` 其余(`index.ts`/`saves.ts`/`use.ts`)与 `ui`/`fx`/`weather`/`render` 顶层文件(D-54);legacy 一律只报告(D-55);用户并发改动零触碰(D-56)。台账按 A 被移集合内部交叉引用 / B 外部导入点(含 `entry-client` / `entry-data` / `packages/legacy-ui` 经 `@user/client-modules` 公共面的间接依赖)/ C barrel 与导出面 / D 分层与循环依赖风险(`client-base` 不得反向依赖 `client-modules`,含 `package.json` 依赖现状)/ E legacy 命中(仅报告)/ F 未确定 六类分组,每行精确到文件或符号 + `file:line` 且 ≥2 锚点;产出 `04-STRUCTURE-MIGRATION-IMPACT.md`,生产代码零改动且**零命名变更**;**已规划待执行**,执行前须 Task 0 汇报关卡等待用户「可以执行」;第二步(移植实施)待用户审阅后另行规划;`REND-01` / `REND-02` 仍 Pending。
+**Plan register note (2026-09-24 · 04-11 第二步):** 第十一个增量 `04-11` = 渲染端结构性重构**第二步·移植实施(代码改动)**(用户裁定 2026-09-24 · D-58..D-68)。**移植按 MOVE 落地**:把 D-52 的 **39** 文件(`render/components` 13 / `render/elements` 5 / `render/map` 20 / `render/utils/layout.ts` 1)按 D-53 放进 `packages-user/client-base/src/{components,elements,map}/` 与 `client-base/src/layout/layout.ts`(并新增 `layout/index.ts`,D-61),**并删除 `client-modules/src/render/` 下的原件**;D-58 另新建 `client-base/src/shared/`(从 `client-modules/src/shared.ts` 摘取被移 `map` 文件所需的 10 个常量,原文件保留);D-63 `client-base/src/index.ts` 新增 `./components` / `./elements` / `./map` / `./layout` / `./shared` 五条桶导出,实现层消费者(`render/ui` 8 文件 11 处含 `toolbar.tsx` 两处深层导入、`fallback/ui.ts:2`、`render/index.tsx:5,45,46`、`client.ts:25`、`types.ts:2`)改指 `@user/client-base`;被移 `map` 文件的 shared 说明符改指 4 处(D-58);D-59 / D-68 的架构耦合(`render/renderer` 3 处 + `render/use.ts` 5 处)与 D-60 的 `render/utils/` 断链(`index.ts:1` / `saves.ts:2`)共 **10** 处以带原因的 `// @ts-expect-error` 暂记,**不反向引用、不解耦**;D-66 `client-base/package.json` 新增 5 条 `workspace:*` 依赖(`@motajs/animate` / `@motajs/common` / `@motajs/render-vue` / `@motajs/system` / `@user/data-common`)并执行 `pnpm i`;D-64 `?raw` 不处理、D-65 legacy 一律不管、D-62 命名一律不变;门禁 = 目标落位 / 移动已发生 / 桶导出 / 依赖与 lockfile / 消费者改指 / 反向引用零命中 / `@ts-expect-error` 落点(10 处新增 + 既有 7 条保留)/ 39 文件允许差异 / 实现层差异 / 范围与用户改动 / 无 `import type` / CRLF 共 12 道 + 计划文件范围门禁 + 人工复核(**D-68 明示不要求可运行、不要求零类型错误,故不设 `check:type` / `build` / TS 诊断数类门禁**);`autonomous: false`(Task 0 汇报关卡);第二步的后续余留(自包引用未验证 / 两行同源星导出 / 8 处反向耦合接线 / D-60 断链)由用户接手;`REND-01` / `REND-02` 仍 Pending。
**Plan register note (2026-09-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:
@@ -224,6 +225,10 @@ Plans:
- [x] 04-10-PLAN.md — 渲染端结构性重构影响范围清点(**只读**,D-51..D-57 第一步):把 `packages-user/client-modules/src/render/` 的通用内容(`render/components`、`render/elements`、`render/map` 三文件夹 + `render/utils/layout.ts`,规划日实测 **39** 文件)迁往渲染系统层 `packages-user/client-base/src/`(D-51/D-52),目标布局按 D-53(`client-base/src/{components,elements,map}/` + `client-base/src/layout/`,不新建 `render/`);不移植 `render/utils/` 其余(`index.ts`/`saves.ts`/`use.ts`)与 `ui`/`fx`/`weather`/`render` 顶层文件(D-54);legacy 只报告(D-55)、用户并发改动零触碰(D-56)。台账按 A 被移集合内部交叉引用(8 方向:`components→elements`、`elements→map`、→ `render/renderer`、→ `render/use.ts`、→ `client-modules/src/shared.ts`、→ `@user/client-base` 自包引用、`map/extension/*→map/types`、零命中机器证据)/ B 外部导入点(`client-modules/src/{client.ts,types.ts}`、`render/index.tsx`、`render/ui` 8 文件 11 处含深层导入、`render/utils/{saves.ts,index.ts}`、`fallback/ui.ts`,以及 `entry-client` / `entry-data` / `packages/legacy-ui` 经公共面的间接依赖)/ C barrel 与导出面(7 节点 + 断链判定)/ D 分层与循环依赖风险(`client-base` 不得反向依赖 `client-modules`;`client-base/package.json` 依赖缺口;`?raw` shader;`dev.md` 规则对照)/ E legacy 命中(仅报告)/ F 未确定 六类分组,每行 ≥2 个 `file:line` 锚点;产出 `04-STRUCTURE-MIGRATION-IMPACT.md`,生产代码零改动且零命名变更;门禁 = 只读基线逐条一致 + 10 节结构 / 39 文件清单 / 类别行数与双锚点 / 两处零命中机器证据 / CRLF / 既有产物保护与计划范围 + 人工复核;`autonomous: false`(Task 0 汇报关卡);第二步(移植实施)待用户审阅后另行规划;`REND-01` / `REND-02` 仍 Pending
+**Wave 10** *(blocked on Wave 9 completion)*
+
+- [ ] 04-11-PLAN.md — 渲染端结构性重构·**第二步·移植实施(代码改动)**(D-58..D-68):把 D-52 的 **39** 文件(`render/components` 13 / `render/elements` 5 / `render/map` 20 / `render/utils/layout.ts` 1)按 D-53 放进 `packages-user/client-base/src/{components,elements,map}/` 与 `client-base/src/layout/layout.ts`(新增 `layout/index.ts`,D-61)并**删除 `client-modules/src/render/` 下的原件**(MOVE);D-58 新建 `client-base/src/shared/`(摘取被移 `map` 文件所需的 10 个常量,`client-modules/src/shared.ts` 保留)并把被移 `map` 文件的 shared 说明符改指 4 处(`'../shared'` × 3 / `'../../shared'` × 1);D-63 `client-base/src/index.ts` 新增 `./components` / `./elements` / `./map` / `./layout` / `./shared` 五条桶导出,实现层消费者(`render/ui` 8 文件 11 处含 `toolbar.tsx` 两处深层导入、`fallback/ui.ts:2`、`render/index.tsx:5,45,46`、`client.ts:25`、`types.ts:2`)改指 `@user/client-base`;D-59 / D-68 的 `render/renderer`(3 处)与 `render/use.ts`(5 处)耦合、D-60 的 `render/utils/`(`index.ts:1` / `saves.ts:2`)断链共 **10** 处加带原因 `// @ts-expect-error`,**不反向引用、不解耦**;D-66 `client-base/package.json` 新增 5 条 `workspace:*` 依赖(`@motajs/animate` / `@motajs/common` / `@motajs/render-vue` / `@motajs/system` / `@user/data-common`)并执行 `pnpm i`;D-64 `?raw` 不处理、D-65 legacy 一律不管、D-62 命名一律不变;门禁 = 目标落位 / 移动已发生 / 桶导出 / 依赖与 lockfile / 消费者改指 / 反向引用零命中(`@user/client-modules` 于 `client-base`)/ `@ts-expect-error` 落点(10 新增 + 7 既有)/ 39 文件允许差异 / 实现层差异 / 范围与用户改动 / 无 `import type` / CRLF 共 12 道 + 计划文件范围门禁 + 人工复核(**不设 `check:type` / `build` / TS 诊断数类门禁**,D-68);`autonomous: false`(Task 0 汇报关卡等待用户「可以执行」);`REND-01` / `REND-02` 仍 Pending
+
**UI hint**: yes
### Phase 5: Legacy 移植
diff --git a/.planning/phases/04-render-adaptation/04-11-PLAN.md b/.planning/phases/04-render-adaptation/04-11-PLAN.md
new file mode 100644
index 0000000..10159bd
--- /dev/null
+++ b/.planning/phases/04-render-adaptation/04-11-PLAN.md
@@ -0,0 +1,762 @@
+---
+phase: 04-render-adaptation
+plan: 11
+type: execute
+wave: 10
+depends_on:
+ - 04-01
+ - 04-02
+ - 04-03
+ - 04-04
+ - 04-05
+ - 04-06
+ - 04-07
+ - 04-08
+ - 04-09
+ - 04-10
+files_modified:
+ # ---- client-base 系统层新增(41)----
+ - packages-user/client-base/src/components/choices.tsx
+ - packages-user/client-base/src/components/floorSelect.tsx
+ - packages-user/client-base/src/components/icons.tsx
+ - packages-user/client-base/src/components/index.ts
+ - packages-user/client-base/src/components/input.tsx
+ - packages-user/client-base/src/components/list.tsx
+ - packages-user/client-base/src/components/misc.tsx
+ - packages-user/client-base/src/components/page.tsx
+ - packages-user/client-base/src/components/scroll.tsx
+ - packages-user/client-base/src/components/textbox.tsx
+ - packages-user/client-base/src/components/textboxTyper.ts
+ - packages-user/client-base/src/components/thumbnail.tsx
+ - packages-user/client-base/src/components/tip.tsx
+ - packages-user/client-base/src/elements/cache.ts
+ - packages-user/client-base/src/elements/index.ts
+ - packages-user/client-base/src/elements/misc.ts
+ - packages-user/client-base/src/elements/props.ts
+ - packages-user/client-base/src/elements/types.ts
+ - packages-user/client-base/src/map/block.ts
+ - packages-user/client-base/src/map/constant.ts
+ - packages-user/client-base/src/map/element.ts
+ - packages-user/client-base/src/map/index.ts
+ - packages-user/client-base/src/map/moving.ts
+ - packages-user/client-base/src/map/renderer.ts
+ - packages-user/client-base/src/map/status.ts
+ - packages-user/client-base/src/map/types.ts
+ - packages-user/client-base/src/map/vertex.ts
+ - packages-user/client-base/src/map/viewport.ts
+ - packages-user/client-base/src/map/extension/door.ts
+ - packages-user/client-base/src/map/extension/hero.ts
+ - packages-user/client-base/src/map/extension/index.ts
+ - packages-user/client-base/src/map/extension/manager.ts
+ - packages-user/client-base/src/map/extension/text.ts
+ - packages-user/client-base/src/map/extension/types.ts
+ - packages-user/client-base/src/map/shader/back.frag
+ - packages-user/client-base/src/map/shader/back.vert
+ - packages-user/client-base/src/map/shader/map.frag
+ - packages-user/client-base/src/map/shader/map.vert
+ - packages-user/client-base/src/layout/layout.ts
+ - packages-user/client-base/src/layout/index.ts
+ - packages-user/client-base/src/shared/shared.ts
+ - packages-user/client-base/src/shared/index.ts
+ # ---- client-base 系统层修改(2)----
+ - packages-user/client-base/src/index.ts
+ - packages-user/client-base/package.json
+ # ---- 根清单(1,经 pnpm i 同步)----
+ - pnpm-lock.yaml
+ # ---- 实现层消费者改指 / @ts-expect-error(14)----
+ - packages-user/client-modules/src/client.ts
+ - packages-user/client-modules/src/types.ts
+ - packages-user/client-modules/src/render/index.tsx
+ - packages-user/client-modules/src/render/ui/main.tsx
+ - packages-user/client-modules/src/render/ui/save.tsx
+ - packages-user/client-modules/src/render/ui/settings.tsx
+ - packages-user/client-modules/src/render/ui/statistics.tsx
+ - packages-user/client-modules/src/render/ui/statusBar.tsx
+ - packages-user/client-modules/src/render/ui/title.tsx
+ - packages-user/client-modules/src/render/ui/toolbar.tsx
+ - packages-user/client-modules/src/render/ui/viewmap.tsx
+ - packages-user/client-modules/src/render/utils/index.ts
+ - packages-user/client-modules/src/render/utils/saves.ts
+ - packages-user/client-modules/src/fallback/ui.ts
+files_deleted:
+ - packages-user/client-modules/src/render/components/choices.tsx
+ - packages-user/client-modules/src/render/components/floorSelect.tsx
+ - packages-user/client-modules/src/render/components/icons.tsx
+ - packages-user/client-modules/src/render/components/index.ts
+ - packages-user/client-modules/src/render/components/input.tsx
+ - packages-user/client-modules/src/render/components/list.tsx
+ - packages-user/client-modules/src/render/components/misc.tsx
+ - packages-user/client-modules/src/render/components/page.tsx
+ - packages-user/client-modules/src/render/components/scroll.tsx
+ - packages-user/client-modules/src/render/components/textbox.tsx
+ - packages-user/client-modules/src/render/components/textboxTyper.ts
+ - packages-user/client-modules/src/render/components/thumbnail.tsx
+ - packages-user/client-modules/src/render/components/tip.tsx
+ - packages-user/client-modules/src/render/elements/cache.ts
+ - packages-user/client-modules/src/render/elements/index.ts
+ - packages-user/client-modules/src/render/elements/misc.ts
+ - packages-user/client-modules/src/render/elements/props.ts
+ - packages-user/client-modules/src/render/elements/types.ts
+ - packages-user/client-modules/src/render/map/block.ts
+ - packages-user/client-modules/src/render/map/constant.ts
+ - packages-user/client-modules/src/render/map/element.ts
+ - packages-user/client-modules/src/render/map/index.ts
+ - packages-user/client-modules/src/render/map/moving.ts
+ - packages-user/client-modules/src/render/map/renderer.ts
+ - packages-user/client-modules/src/render/map/status.ts
+ - packages-user/client-modules/src/render/map/types.ts
+ - packages-user/client-modules/src/render/map/vertex.ts
+ - packages-user/client-modules/src/render/map/viewport.ts
+ - packages-user/client-modules/src/render/map/extension/door.ts
+ - packages-user/client-modules/src/render/map/extension/hero.ts
+ - packages-user/client-modules/src/render/map/extension/index.ts
+ - packages-user/client-modules/src/render/map/extension/manager.ts
+ - packages-user/client-modules/src/render/map/extension/text.ts
+ - packages-user/client-modules/src/render/map/extension/types.ts
+ - packages-user/client-modules/src/render/map/shader/back.frag
+ - packages-user/client-modules/src/render/map/shader/back.vert
+ - packages-user/client-modules/src/render/map/shader/map.frag
+ - packages-user/client-modules/src/render/map/shader/map.vert
+ - packages-user/client-modules/src/render/utils/layout.ts
+autonomous: false
+requirements:
+ - REND-01
+ - REND-02
+estimate:
+ tokens: 130000
+ raw_tokens: 130000
+ tasks: 5
+ confidence: low
+must_haves:
+ truths:
+ - "**本计划的第一个 block 点是 Task 0 的 `blocking-human` 汇报关卡(AGENTS.md 硬要求)**:Task 0 必须先向用户汇报「要解决的问题(`packages-user/client-modules/src/render/` 下的通用渲染内容仍留在实现层,D-51 的分层纠正尚未落地)、锁定裁定(D-58..D-68 逐条)、大致解决方案(**移植 = MOVE**:把 39 个文件按 D-53 目标布局放到 `client-base/src/` 下并**删除 `client-modules/src/render/` 下的原件**;D-58 新建 `client-base/src/shared/`;D-61 新建 `layout/{layout,index}.ts`;D-63 目标桶 + 实现层消费者改指 `@user/client-base`;D-59/D-60/D-67 架构耦合处以 `// @ts-expect-error`(附原因)暂记;D-62 命名一律不变;D-64 `?raw` 不处理;D-65 legacy 一律不管;D-66 补依赖并执行 `pnpm i`;D-68 只做移植、**不要求可运行 / 不要求零类型错误**)」,并**逐条明示三处需用户当场否决的解读**(① 移植按 MOVE 而非 copy-and-leave;② D-59 未点名的 `render/use.ts` 方向按 D-68 的兜底规则同样用 `// @ts-expect-error`;③ `client-base/src/shared/` 采用 `shared/shared.ts` + `shared/index.ts` 两文件形态以对齐 D-61 的 `layout/` 先例),然后必须**停下等待**用户明确回复「可以执行」四个字;收到该回复前不得执行任何任务、不得派发任何执行子代理。无论触发方式(`/gsd-execute-phase`、`/gsd-manager`、`/gsd-autonomous`、子代理/执行器派发)或配置模式(`.planning/config.json` 的 `mode: yolo`、`workflow.auto_advance`)如何,均不得自动放行。"
+ - "**移植即移动(MOVE)并落地 39 个目标文件**:Task 1 / 2 / 3 结束时,`components` 13 / `elements` 5 / `map` 20 / `layout` 1 共 **39** 个源文件按 D-53 目标布局存在于 `packages-user/client-base/src/{components,elements,map}/` 与 `packages-user/client-base/src/layout/` 下(另加 D-61 新增的 `layout/index.ts`、D-58 新增的 `shared/shared.ts` 与 `shared/index.ts`,合计新增 **42** 个文件),且 `packages-user/client-modules/src/render/{components,elements,map}` 三个目录下的原件与 `packages-user/client-modules/src/render/utils/layout.ts` **全部被删除**(`files_deleted` 的 39 条逐条不存在;`render/components` / `render/elements` / `render/map` 目录内不再有任何文件)。若用户否决 MOVE 解读而选择 copy-and-leave,本计划退出并经用户确认后修订。"
+ - "**D-58 shared:系统层新建 `client-base/src/shared/` 且常量集合恰好覆盖被移 `map` 文件所需**:`client-base/src/shared/shared.ts` 内含 `CELL_WIDTH` / `CELL_HEIGHT` / `CELL_SIZE` / `MAP_BLOCK_WIDTH` / `MAP_BLOCK_HEIGHT` / `MAP_WIDTH` / `MAP_HEIGHT` / `DYNAMIC_RESERVE` / `MOVING_TOLERANCE` / `DOOR_ANIMATE_INTERVAL` 十个常量(含 `MAP_WIDTH` / `MAP_HEIGHT` 的传递依赖 `CELL_SIZE` / `MAP_BLOCK_*`),逐行取自 `packages-user/client-modules/src/shared.ts` 且 jsDoc / `//#region` 注释逐字保留;`shared/index.ts` 为 `export * from './shared'`;`packages-user/client-modules/src/shared.ts` **保留不动**(供留在实现层的 `render/utils/saves.ts` 等消费者);被移 `map` 文件的 shared 引用改指为:`map/{element,renderer,vertex}.ts` 由 `'../../shared'` 改为 `'../shared'`、`map/extension/door.ts` 由 `'../../../shared'` 改为 `'../../shared'`。"
+ - "**D-59 + D-68:`render/renderer`(`mainRenderer` / `tagManager` / `using`)与 `render/use.ts` 方向一律不反向引用、不解耦,只加 `// @ts-expect-error`(附简要原因)**:被移文件中 8 处此类 import 保留原相对说明符文本不变并在其**上一行**新增 `// @ts-expect-error <原因>`——`elements/index.ts`(`mainRenderer` / `tagManager`)、`components/textboxTyper.ts`(`using`)、`components/misc.tsx`(`using`)来自 `'../renderer'`;`components/{choices,input,misc,scroll,tip}.tsx`(`useKey` / `transitioned` / `transitionedColor`)来自 `'../use'`。**不得**新增 `from '@user/client-modules'` 形式的反向引用,**不得**把 `mainRenderer` / `tagManager` / `using` / `useKey` / `transitioned` / `transitionedColor` 下沉、抽类型或改签名。"
+ - "**D-60:`render/utils/` 不移植,其指向被移内容的 2 处引用以 `// @ts-expect-error`(附原因)处理**:`packages-user/client-modules/src/render/utils/index.ts:1`(`export * from './layout'`,`layout.ts` 已移走)与其后 `saves.ts:2`(`import { getConfirm, waitbox } from '../components'`)各自保留原文本并在上一行新增 `// @ts-expect-error <原因>`;`render/utils/use.ts` 未指向被移内容,零改动;`render/utils/` 三个文件均**不得**改为引用 `@user/client-base`。"
+ - "**D-61 + D-62:`layout` 双文件 + 命名一律不变**:`packages-user/client-base/src/layout/layout.ts` 为 `packages-user/client-modules/src/render/utils/layout.ts` 的逐字副本(唯一允许的差异为零——该文件只 import `'@motajs/render'`,无被移集合内部引用、无 legacy 命中),`packages-user/client-base/src/layout/index.ts` 为 `export * from './layout'`。**全部 39 个被移文件 + 5 个新建桶/shared 文件的文件名与目录名与 D-62 一致(不重命名、不改大小写、不动扩展名)**;被移文件相对原件的内容差异**仅限**三项:shared 说明符改写、`// @ts-expect-error` 插入、`'../renderer'` / `'../use'` 说明符所在行的保留——由「允许差异门禁」以正则可允许集逐行判定。"
+ - "**D-63:`client-base/src/index.ts` 新增 5 条桶导出,实现层消费者直接引用 `@user/client-base`**:`packages-user/client-base/src/index.ts` 在保留既有 `export * from './load' | './material' | './save' | './types'` 四行原文的前提下新增 `export * from './components' | './elements' | './map' | './layout' | './shared'` 五行;实现层消费者改为直接引用 `@user/client-base`——`render/ui/` **8** 个文件的 **11** 处(`main.tsx:9` / `save.tsx:16` / `settings.tsx:16,21` / `statistics.tsx:8` / `statusBar.tsx:3` / `title.tsx:31` / `viewmap.tsx:26,29` / `toolbar.tsx:13,19`,其中 `toolbar.tsx` 两处为深层导入 `'../components/icons'` 与 `'../components/misc'`)、`fallback/ui.ts:2`(深层导入 `'../render/components/tip'`)、`render/index.tsx:5`(`createElements`)与 `:45` / `:46`(`export * from './components'` / `'./elements'`)、`client-modules/src/client.ts:25` 与 `client-modules/src/types.ts:2`(`'./render/map'`);改指一律**只替换说明符文本为 `'@user/client-base'`**,不合并 / 不重排 / 不拆分 import 语句。`render/index.tsx` 的 `:45` / `:46` 改指后成为两行同源 `export * from '@user/client-base'`(D-68:不要求零类型错误,该形态保留并由 SUMMARY 如实登记,是否合并由用户后续裁定)。"
+ - "**D-66:`client-base/package.json` 补依赖并执行 `pnpm i`**:`packages-user/client-base/package.json` 的 `dependencies` 新增 `@motajs/animate` / `@motajs/common` / `@motajs/render-vue` / `@motajs/system` / `@user/data-common`(均为 `workspace:*`,对齐 `client-modules/package.json` 的声明风格);保留既有 5 条不删;`pnpm i` 在仓库根执行且 `pnpm-lock.yaml` 的 `packages-user/client-base` importer 块同步出现上述 5 条;根级已声明的第三方包(`vue` / `lodash-es` / `eventemitter3` / `mutate-animate`)按项目既有惯例**不**在包内重复声明。"
+ - "**D-67:`createElements()` 的标签 / 元件注册随 `elements/index.ts` 一并移植,其耦合错误以 `// @ts-expect-error` 暂记**:`client-base/src/elements/index.ts` 的 `createElements()` 及其 `mainRenderer.registerElement(...)` × 3 / `tagManager.registerTag(...)` × 3 注册体逐字随文件移动、逻辑零改动;其 `'../renderer'` import 由 D-59 的 `@ts-expect-error` 覆盖(导入绑定降级为 `any`,故注册体本身不再新增标注)。"
+ - "**D-68:本步只做移植——不要求可运行、不要求零类型错误**:验证一律为静态门禁(路径存在 / 原件删除 / 桶导出 / 依赖与 lockfile / 消费者改指 / 反向引用零命中 / `@ts-expect-error` 落点 / 允许差异 / 范围与用户改动 / CRLF),**不得**出现任何以「全仓 TS 诊断归零」「`pnpm check:type` 通过」「`pnpm build` 成功」「运行时 UIG 观察」为条件的门禁或验收项;也不得因这类检查失败而阻塞交付。"
+ - "**分层红线(D-57 的硬约束在第二步的延续):`client-base` 零反向引用 `client-modules`**:`git grep -n -F \"@user/client-modules\" -- packages-user/client-base` 与 `git grep -n -F \"client-modules\" -- packages-user/client-base` 均须**零命中**(退出码 1);被移文件的 `@user/client-base` 自包引用(`map/extension/hero.ts:16` / `map/moving.ts:3` / `map/renderer.ts:13` / `map/types.ts:12` / `map/vertex.ts:28`)说明符**保持原文不动**且**不为它们新增 `@ts-expect-error`**(同包引用不属 D-59/D-60/D-67 的架构耦合面)。"
+ - "**范围零外溢:`material/` / legacy / `packages/` / `src/` / 用户并发改动全部零触碰**:`git status --porcelain -- packages-user/client-base/src/material packages-user/client-base/src/load packages src` 为空;不修任何 legacy 报错(D-55 / D-65);Task 1 起始逐行记录 `git status --porcelain -- packages packages-user src` 作为只读起始基线(D-56),Task 1 / 4 断言其后**仅新增本计划 `files_modified` / `files_deleted` 范围内的条目**、既有条目逐条一致,并断言 `.planning/phases/04-render-adaptation/04-CONTEXT.md` 的既有未提交改动仍在。"
+ - "**`@ts-expect-error` 全部带原因且仅出现在授权落点**:`packages-user/client-base/src/**` 与本次改动的 `packages-user/client-modules/src/**` 内**每一行** `// @ts-expect-error` 均形如 `// @ts-expect-error <非空原因>`(`dev.md`:无法避免类型错误时标记并说明原因);新增标注**仅**落在 D-59(`renderer` 3 处)+ D-59/D-68(`use` 5 处)+ D-60(`render/utils` 2 处)共 **10** 处,被移文件内既有 7 条标注(`components/textbox.tsx` / `components/textboxTyper.ts` / `elements/cache.ts` × 4 / `map/extension/hero.ts`)**逐字保留**、不增不减。"
+ - "**REND-01 / REND-02 在本 run 后仍保持 Pending**:本步只纠正分层(把通用渲染资产移入系统层),既未实施渲染端接口适配,也未实施移动端 / 桌面端双布局(见 assumptions 的两条 FLAGGED ASSUMPTION);`.planning/REQUIREMENTS.md` 零改动。"
+ artifacts:
+ - path: packages-user/client-base/src/index.ts
+ provides: "渲染系统层出口新增 5 条桶导出(`./components` / `./elements` / `./map` / `./layout` / `./shared`),使被移的通用渲染资产与新建桶/shared 可从 `@user/client-base` 直接消费(D-63);既有 4 条 `./load` / `./material` / `./save` / `./types` 原文保留"
+ - path: packages-user/client-base/src/layout/layout.ts
+ provides: "端到端切片(tracer)对象:`render/utils/layout.ts` 的逐字副本(`adjustGrid` / `adjustCover` / `IGridLayoutData`),只依赖既有 `'@motajs/render'`;配套新桶 `packages-user/client-base/src/layout/index.ts`(D-61)"
+ - path: packages-user/client-base/src/shared/shared.ts
+ provides: "D-58 的系统层常量面:从 `client-modules/src/shared.ts` 摘取的 10 个被移 `map` 文件实际需要的常量(含传递依赖),jsDoc / region 注释逐字保留;配套新桶 `packages-user/client-base/src/shared/index.ts`"
+ - path: packages-user/client-base/src/map/
+ provides: "20 个地图渲染文件(顶层 10 + `extension/` 6 + `shader/` 4)的 D-53 目标落点;shared 引用已按 D-58 改指;`?raw` 说明符相对路径不变(D-64);`@user/client-base` 自包引用保持不变"
+ - path: packages-user/client-base/src/components/
+ provides: "13 个通用渲染组件(含 barrel `index.ts` 的 12 条 `export *`)的 D-53 目标落点;`'../renderer'` / `'../use'` 3 类耦合以 `// @ts-expect-error` 暂记(D-59 / D-68)"
+ - path: packages-user/client-base/src/elements/
+ provides: "5 个渲染元素文件的 D-53 目标落点;`createElements()` 标签 / 元件注册随文件移植、逻辑零改动、耦合以 `// @ts-expect-error` 暂记(D-67)"
+ - path: packages-user/client-base/package.json
+ provides: "新增 5 条 `workspace:*` 依赖(`@motajs/animate` / `@motajs/common` / `@motajs/render-vue` / `@motajs/system` / `@user/data-common`),承托被移集合的外部依赖面(D-66)"
+ key_links:
+ - "`packages-user/client-modules/src/render/{components,elements,map}/` + `render/utils/layout.ts`(39 文件,D-52)↔ `packages-user/client-base/src/{components,elements,map,layout,shared}/`(D-53 + D-58 + D-61)—— 本计划的核心移动;原件删除后由「原件不存在」门禁证明 MOVE 已发生"
+ - "`packages-user/client-base/src/index.ts`(新增 5 条桶导出)↔ `packages-user/client-modules/src/render/ui/{main,save,settings,statistics,statusBar,title,toolbar,viewmap}.tsx`(11 处 import 改指)/ `fallback/ui.ts:2`(深层导入改指)/ `render/index.tsx:5,45,46` / `client-modules/src/client.ts:25` / `client-modules/src/types.ts:2` —— D-63 的消费者改指链,保证 `@user/client-modules` 公共面不收窄"
+ - "`packages-user/client-modules/src/render/index.tsx:45,46`(改指后为两行同源 `export * from '@user/client-base'`)↔ `packages-user/client-modules/src/index.ts:3,15`(`import { createGameRenderer, createRender } from './render'` 与 `export * from './render'`)↔ `entry-client/src/create.ts:10,27` / `entry-data/src/mota.ts:11,32` —— 公共面链路的连续性;该三处外部包**零改动**"
+ - "`packages-user/client-modules/src/render/utils/index.ts:1`(`export * from './layout'`)与 `render/utils/saves.ts:2`(`'../components'`)↔ D-60 的 2 处 `// @ts-expect-error` —— 明确「不移植 barrel 的断链以标注暂记、不反向引用」的分层处置"
+ - "`packages-user/client-modules/src/render/{renderer.ts,use.ts}` ↔ `packages-user/client-base/src/{elements/index.ts:3 → mainRenderer/tagManager, components/{misc.tsx,textboxTyper.ts} → using}` 与 `components/{choices,input,misc,scroll,tip}.tsx → useKey/transitioned/transitionedColor` —— D-59 / D-68 的「不反向引用、不解耦、只标注」面(8 处)"
+ - "`packages-user/client-modules/src/shared.ts`(保留)↔ `packages-user/client-base/src/shared/shared.ts`(新增,10 常量)↔ 被移 `map` 文件的 shared 说明符(`'../shared'` / `'../../shared'`)—— D-58 的双向分离:实现层常量留原地,系统层只带走被移集合实际需要的那一份"
+ - "`packages-user/client-base/package.json`(+5 依赖)↔ `pnpm-lock.yaml`(`packages-user/client-base` importer 块)—— D-66 的 `pnpm i` 证据链"
+ - "`git status --porcelain -- packages packages-user src`(Task 1 步骤 (0a) 逐行记录)↔ Task 1 / Task 4 门禁的「既有条目逐条一致 + 仅新增计划内条目 + `04-CONTEXT.md` 未提交改动仍在」断言 ↔ D-56 与只读证明"
+ assumptions:
+ - "**FLAGGED ASSUMPTION(specless fallback)— REND-01 edge unresolved:** 未提供 REND-01(渲染端适配新数据层接口)的验收边界;本计划**不发明**阈值,只交付「通用渲染资产下沉系统层」的移植实施切片(D-58..D-68)。用户须在推进 REND-01 余下部分前补齐其边界判据。"
+ - "**FLAGGED ASSUMPTION(specless fallback)— REND-02 edge unresolved:** 未提供 REND-02(移动端与桌面端双布局)的验收边界;本计划不涉及双布局,也不发明其验收阈值。"
+ - "**移植语义按 MOVE 解读(须用户在 Task 0 确认):** 「移植」在本计划中落地为「按 D-53 目标布局放置 39 个文件**并删除** `client-modules/src/render/` 下的原件」。理由:若只复制不删除,`client-modules/src/render/` 下会留下同名死副本,D-63 的「实现层消费者改指 `@user/client-base`」将无实际意义且留下两套真相。**若用户意图为 copy-and-leave,Task 0 中否决即可**,本计划退出并由用户确认后修订(`files_deleted` 相应清空)。"
+ - "**`render/use.ts` 方向按 D-68 兜底(须用户在 Task 0 确认):** D-59 只点名 `render/renderer`,D-60 只点名 `render/utils/`。`components/*` 对 `render/use.ts`(`useKey` / `transitioned` / `transitionedColor`)的 5 处 import 与 `render/renderer` 属同一性质(留在实现层的模块被系统层反向引用),故按 D-59 的原则 + D-68「架构耦合 / 无法立刻正确接线处一律 `// @ts-expect-error` 暂记」同样处理。**用户若认为该方向应另案处理,Task 0 中否决即可。**"
+ - "**`client-base/src/shared/` 的文件形态(须用户在 Task 0 确认):** 采用 `shared/shared.ts`(常量)+ `shared/index.ts`(`export * from './shared'`)两文件,以对齐 D-61 对 `layout/` 给出的「内容文件 + barrel」先例,并满足 D-63 对 `./shared` 的桶导出要求。若用户希望改为单文件 `shared/index.ts`,Task 0 中指示即可。"
+ - "**被移文件的 `@user/client-base` 自包引用说明符保持原文(不新增标注):** 5 处自包引用在移入后成为包内自引用;D-59 / D-60 / D-67 未覆盖该方向,D-63 只规范「实现层消费者」,故一律**只搬不改**、也不加 `// @ts-expect-error`。若自引用在构建/解析上不成立,属 D-68 明示「不要求可运行」的余留,由用户后续裁定(SUMMARY 如实登记)。"
+ - "**执行日行号可能漂移:** `04-STRUCTURE-MIGRATION-IMPACT.md`(04-10)的 `file:line` 是 2026-09-24 的锚点;用户会并发改其它文件(D-56)。执行时一律以**当日实测**定位(按符号名 / 说明符文本检索),不得按本计划的规划日行号硬写;任一与规划日不同即在 SUMMARY 记录差异。"
+ - "**无运行时验证可得(D-68 明示):** 本步「没有『保证能运行』的要求」。渲染端无运行时测试设施(04-07 / 04-08 / 04-09 已如实声明),本计划也**不新增任何测试**;承载体为静态门禁 + 允许差异比对 + 人工复核。**不得**以 `pnpm check:type` / `pnpm build` / 全仓诊断数之类的条件冒充门禁。"
+ - "**用户可能有执行日的并发未提交改动(D-56):** 规划日实测基线为**单条** `.planning/phases/04-render-adaptation/04-CONTEXT.md`(不在 `packages` / `packages-user` / `src` 内);Task 1 步骤 (0a) 须逐行记录当日实测,并在 Task 4 断言既有条目不消失、不新增计划外条目。**不得回滚 / 暂存 / 提交 / 修改**用户任何未提交改动。"
+ - "**`pnpm i` 的副作用面:** 执行 `pnpm i` 会改写 `pnpm-lock.yaml` 并可能影响 `node_modules/`;这属 D-66 明确授权的改动,但**不得**顺带格式化 / 重排 lockfile 之外的内容,也不得改动任何其它 `package.json`。`node_modules/` 不入库、不需提交。"
+ - "**`.planning/graphs/*` 是过期工装态:** 其中的分层 / 依赖标签早于本次结构性重构;该文件**不得**作为基准或命中依据,也不得作为门禁的扫描面(是否重建由用户决定)。"
+ prohibitions:
+ - "不得在用户明确回复「可以执行」四个字之前执行本计划的任何任务、或派发任何执行子代理(AGENTS.md 的第一个 block 点约束;不因触发方式或 `yolo` / `auto_advance` 模式而放宽)。"
+ - "不得修改 `packages-user/client-base/src/material/**`(D-42 / D-48)、`packages-user/client-base/src/load/**`、`packages-user/client-modules/src/{action,fx,weather,scene,renderer.ts,use.ts,index.tsx}` 的既有实现(`render/index.tsx` 仅允许 D-63 的说明符改指)、`packages/**`、`src/**`、`tsconfig.json`;不得改动任何其它 `package.json`(只允许 `packages-user/client-base/package.json`)。"
+ - "不得修复任何 legacy 报错(D-55 / D-65):`core.*` / 全局 `loading` / `hook` / `Mota.require` 命中一律不管、不修、不登记为待办;不得因 `packages/legacy-ui` 的插件面顾虑而增加任何兼容代码(D-49 作废该类约束)。"
+ - "不得创建任何反向引用:不得出现 `from '@user/client-modules'` 形式的 import 于 `packages-user/client-base/**`;不得把 `mainRenderer` / `tagManager` / `using` / `useKey` / `transitioned` / `transitionedColor` 下沉、抽类型、改签名、加参数或复制一份到 `client-base`(D-59)。"
+ - "不得为「架构耦合」以外的任何位置新增 `// @ts-expect-error`;不得删除或改写被移文件内既有 7 条标注;不得写无原因的裸 `// @ts-expect-error`(`dev.md`)。"
+ - "不得改写 / 删除任何既有 jsDoc 与注释(AGENTS.md);被移文件的注释文本必须在移动后逐字一致(由「允许差异门禁」逐行判定);不得新增 / 改名任何公共、受保护、私有成员、方法、字段、文件或目录(D-62)。"
+ - "不得使用 `import type`(`dev.md`);不得新增、删除或修改任何测试文件;不得新增依赖到 `packages-user/client-modules/package.json` 或其它包;不得把根级已声明的第三方包(`vue` / `lodash-es` / `eventemitter3` / `mutate-animate`)写进 `client-base/package.json`。"
+ - "不得把任何门禁建立在「全仓 / 范围内 TS 诊断数」「`pnpm check:type`」「`pnpm build`」之上,也不得因这类检查失败而阻塞交付(D-68)。"
+ - "不得改写 / 覆盖 / 改号 `04-01`..`04-10` 的既有 `*-PLAN.md` 与 `*-SUMMARY.md`;不得产出 `04-12+` 或任何新计划文件(本步是 04-10 的后续实施,不再分叉);除本计划的 `files_modified` / `files_deleted`、`04-11-SUMMARY.md` 与 `.planning/ROADMAP.md` 的登记外不得写任何其它文件。"
+ - "不得触碰、暂存、提交用户的并发改动(D-56):尤其不得提交 `.planning/phases/04-render-adaptation/04-CONTEXT.md` 的既有未提交改动;提交前必须以 `git status` 逐条核对暂存面,且只暂存本计划 `files_modified` / `files_deleted` 的条目。"
+ - "不得把 `render/index.tsx:45,46` 改指后出现的「两行同源 `export * from '@user/client-base'`」当成本步的工作项去合并 / 去重(D-68:不要求零类型错误);如实登记即可,是否合并由用户后续裁定。"
+---
+
+
+Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步·移植实施**)—— **渲染端结构性重构·移植实施(把通用渲染资产下沉到渲染系统层)**。
+
+**要解决的问题:**
+
+1. **通用渲染内容仍放错层(D-51 尚未落地)**:04-10 已产出只读影响台账 `04-STRUCTURE-MIGRATION-IMPACT.md`(A9 / B9 / C8 / D10 / E6 / F10),确认 `packages-user/client-modules/src/render/` 下的通用内容(`components` / `elements` / `map` 三文件夹 + `utils/layout.ts`,共 **39** 文件)本属渲染**系统层** `packages-user/client-base/src/`。本步把台账的 A / B / C / D / F 面**逐条按用户裁定执行**,完成分层纠正。
+2. **台账的 F 类已全部裁定(D-58..D-68)**:F-01 命名 → D-61/D-62(`layout/layout.ts` + `layout/index.ts`;命名一律不变);F-02 `render/utils/index.ts` 归属 → D-60(`// @ts-expect-error`);F-03 目标桶 → D-63(`client-base/src/index.ts` 新增 5 条桶导出 + 实现层消费者改指 `@user/client-base`);F-04 反向依赖解耦 → D-58(`shared` 下沉系统层)/ D-59(`renderer` 不处理只标注)/ D-67(标签注册先移植 + 标注);F-05 自包引用 → 本计划按「只搬不改」处置(见 assumptions);F-06 依赖缺口 → D-66(补 5 条依赖 + `pnpm i`);F-07 `?raw` → D-64(不处理);F-08 legacy 插件面 → D-65(一律不管);F-09 `createElements()` 接线 → D-67;F-10 装配链归属 → 装配点 `render/index.tsx` 留在实现层、其 `createElements` 改为从 `@user/client-base` 取(D-63)。
+3. **分层红线**:`client-base` 不得反向依赖 `client-modules`。台账 D-02/03/04/05 登记了 4 族共 13 处反向依赖候选;本步以 D-58(真正下沉 `shared` 常量)+ D-59/D-68(其余只标注不接线)的方式处置,并以「`@user/client-modules` 在 `client-base` 内零命中」的机器门禁证明红线未破。
+4. **本步是纯结构迁移(D-68)**:没有「保证能运行」的要求,也没有「范围内 TS 报错清零」的要求;架构耦合 / 无法立刻正确接线处一律 `// @ts-expect-error`(附原因)暂记,由用户后续收尾。**效率优先,细节不纠结。**
+
+**大致解决方案(移植 = MOVE):**
+
+把 D-52 的 **39** 个文件按 D-53 / D-61 目标布局放到 `packages-user/client-base/src/` 下,**并删除 `client-modules/src/render/` 下的原件**:
+
+| 源(删除) | 目标(新增) |
+|---|---|
+| `client-modules/src/render/components/`(13) | `client-base/src/components/`(13) |
+| `client-modules/src/render/elements/`(5) | `client-base/src/elements/`(5) |
+| `client-modules/src/render/map/`(20,含 `extension/` 6 + `shader/` 4) | `client-base/src/map/`(20) |
+| `client-modules/src/render/utils/layout.ts`(1) | `client-base/src/layout/layout.ts` + 新增 `layout/index.ts`(D-61) |
+| `client-modules/src/shared.ts` 的**所需常量**(保留原文件) | 新增 `client-base/src/shared/shared.ts` + `shared/index.ts`(D-58) |
+
+**引用调整清单(唯一允许的改动面):**
+
+| # | 面 | 处置 |
+|---|---|---|
+| R1 | 被移文件 → `client-modules/src/shared.ts`(`map/{element,renderer,vertex}.ts` 的 `'../../shared'`、`map/extension/door.ts` 的 `'../../../shared'`) | 改指为 `'../shared'` / `'../../shared'`(D-58) |
+| R2 | 被移文件 → `render/renderer`(`elements/index.ts`、`components/{misc.tsx,textboxTyper.ts}` 的 `'../renderer'`) | 文本不变 + 上一行 `// @ts-expect-error <原因>`(D-59) |
+| R3 | 被移文件 → `render/use.ts`(`components/{choices,input,misc,scroll,tip}.tsx` 的 `'../use'`) | 同上(D-59 原则 + D-68 兜底,见 assumptions) |
+| R4 | 被移文件 → `@user/client-base` 自包引用 5 处 | **不动**(不新增标注) |
+| R5 | 被移集合内部相对引用(`'../elements'` / `'../map'` / `'../types'` / `'./*'` / `?raw`) | 移动后相对路径不变 ⇒ **不动**(D-64 对 `?raw` 明确不处理) |
+| R6 | `client-base/src/index.ts` | 新增 5 条桶导出,保留原有 4 行原文(D-63) |
+| R7 | `client-base/package.json` | 新增 5 条 `workspace:*` 依赖(D-66)+ 根执行 `pnpm i` |
+| R8 | 实现层消费者(`render/ui/` 8 文件 11 处、`fallback/ui.ts:2`、`render/index.tsx:5,45,46`、`client.ts:25`、`types.ts:2`) | 说明符改为 `'@user/client-base'`(D-63) |
+| R9 | `render/utils/index.ts:1` 与 `render/utils/saves.ts:2` | 文本不变 + 上一行 `// @ts-expect-error <原因>`(D-60) |
+
+**范围与硬边界:** 被移对象 = 台账 39 文件;唯一新增面 = `client-base/src/{components,elements,map,layout,shared}/**` + `client-base/src/index.ts` + `client-base/package.json` + `pnpm-lock.yaml` + 14 个实现层消费者的引用行;**零触碰** = `client-base/src/material/**`(D-42/D-48)、`client-base/src/load/**`、`client-modules/src/{action,fx,weather,scene,renderer.ts,use.ts,index.tsx}` 的实现(`render/index.tsx` 仅说明符)、`packages/**`、`src/**`、其它 `package.json`、任何测试文件、用户的并发改动(D-56)、legacy 面(D-55 / D-65)。
+
+**任务切分(Task 0 汇报关卡 + 4 个工作任务):** 本计划按 04-10 的交付要求落在**单一计划文件**内(委托指定 `04-11` 为唯一新计划),故未再拆成多份 `*-PLAN.md`;4 个工作任务按「文件夹 + 消费者面」机械切分(tracer = `layout` + `shared` 端到端切片;扩展 = `components`+`elements`;扩展 = `map` + 深层消费者;收口 = 门禁 + 人工复核 + SUMMARY),每个任务都保持在单代理上下文预算内。
+
+## Source Audit(多源覆盖审计)
+
+| SOURCE | ID | Feature/Requirement | Plan | Status | Notes |
+|--------|----|---------------------|------|--------|-------|
+| GOAL | — | Phase 4:「渲染端通过新数据层接口驱动,并同时支持移动端与桌面端布局」 | 04-11 | COVERED(增量·结构性重构实施子切片) | 本 run 只实施「通用渲染资产由 `client-modules` 下沉 `client-base`」;接口适配与双布局属后续增量(见 assumptions 的两条 FLAGGED ASSUMPTION) |
+| REQ | REND-01 | 渲染端适配新数据层接口 | 04-11 | COVERED(子切片·结构前置;需求保持 Pending) | 分层纠正是 REND-01 后续实现的**前置结构条件**(D-51);本 run 不移交任何接口行为;验收边界未提供 → FLAGGED ASSUMPTION |
+| REQ | REND-02 | 渲染端同时支持移动端与桌面端布局 | 04-11 | COVERED(不涉及) | 本 run 范围不含双布局;验收边界未提供 → FLAGGED ASSUMPTION |
+| RESEARCH | — | (本阶段无 `04-RESEARCH.md`) | — | N/A | Phase 4 未产出 RESEARCH.md;等价输入为 `04-STRUCTURE-MIGRATION-IMPACT.md`(04-10 只读台账,A9/B9/C8/D10/E6/F10) |
+| CONTEXT | D-51 / D-52 | 目标 = 通用内容迁移到 `client-base/src/`;移植集合 = `render/components` + `render/elements` + `render/map` 三文件夹 + `render/utils/layout.ts`(39 文件) | 04-11 | COVERED | `files_modified` / `files_deleted` 逐条枚举 39 个源路径与目标路径;任务只搬这 39 个文件 |
+| CONTEXT | D-53 / D-61 | 目标布局:不新建 `render/`;`client-base/src/{components,elements,map}/`;`layout.ts` 单独 `client-base/src/layout/layout.ts` **并新增 `layout/index.ts`** | 04-11 | COVERED | Task 1 tracer 建 `layout/{layout,index}.ts`;Task 2/3 建 `{components,elements,map}/`;门禁断言无 `client-base/src/render/` 目录 |
+| CONTEXT | D-54 | 不移植 `render/utils/` 其余(`index.ts` / `saves.ts` / `use.ts`)与 `ui` / `fx` / `weather` / `render` 顶层文件 | 04-11 | COVERED(边界) | 这些文件**不移动**;其指向被移内容的引用按 D-60 / D-63 处置;prohibitions 明列不得改动其实现 |
+| CONTEXT | D-55 / D-65 | 不考虑任何 legacy:legacy 报错一律不管、不修、不登记为待办 | 04-11 | COVERED(排除) | 无任何 legacy 任务 / 门禁 / 待办;prohibitions 明列;`packages/legacy-ui` 零改动 |
+| CONTEXT | D-56 | 用户会同步进行其它修改;不得触碰、不得提交用户并发改动 | 04-11 | COVERED | Task 1 步骤 (0a) 逐行记录当日基线;Task 1 / Task 4 断言既有条目逐条一致且仅新增计划内条目、`04-CONTEXT.md` 未提交改动仍在;提交前 `git status` 逐条核对 |
+| CONTEXT | D-58 | 系统层新建 `client-base/src/shared/`,把被移 `map` 文件实际需要的常量挪过去;`client-modules/src/shared.ts` 保留 | 04-11 | COVERED | Task 1 步骤 (2) 建 `shared/{shared,index}.ts`(10 常量含传递依赖,注释逐字保留);Task 3 改 shared 说明符 R1;门禁断言 `client-modules/src/shared.ts` 仍在 |
+| CONTEXT | D-59 | `render/renderer`(`mainRenderer` / `tagManager` / `using`)先不处理、不做反向引用、不做解耦;相应导入 / 使用处以 `// @ts-expect-error`(附原因)忽略 | 04-11 | COVERED | Task 1/2 的 R2:3 处 import 加标注;门禁断言 `client-base` 内 `@user/client-modules` 零命中 |
+| CONTEXT | D-60 | `render/utils/` 不移植;其指向被移内容的引用以 `// @ts-expect-error` 处理 | 04-11 | COVERED | Task 1(`index.ts:1`)+ Task 3(`saves.ts:2`)的 R9 共 2 处标注 |
+| CONTEXT | D-62 | 文件名 / 目录名与原先一致;操作方式 = 直接复制文件,然后调整引用 | 04-11 | COVERED | 无任何重命名;「允许差异门禁」逐行限定改动面为 R1/R2/R3;Task 0 汇报该性质 |
+| CONTEXT | D-63 | `client-base/src/index.ts` 新增 `./components` / `./elements` / `./map` / `./layout` / `./shared` 桶导出;实现层消费者改为直接引用 `@user/client-base` | 04-11 | COVERED | R6 桶(Task 1 加 2 条、Task 2 加 2 条、Task 3 加 1 条)+ R8 全部 14 个消费者文件;门禁断言 5 条说明符存在且原有 4 行保留 |
+| CONTEXT | D-64 | shader 的 `?raw` 引用可正常执行,不需要处理 | 04-11 | COVERED(无动作) | R5:`map/shader/*` 相对路径不变;`map/renderer.ts` 的 4 条 `?raw` import 逐字随文件移动;无任何 `?raw` 相关任务或门禁 |
+| CONTEXT | D-66 | `client-base/package.json` 新增所需依赖并执行 `pnpm i` | 04-11 | COVERED | R7:新增 5 条 `workspace:*`;Task 1 步骤 (4) 在根执行 `pnpm i`;门禁断言 deps 与 lockfile importer 块同步 |
+| CONTEXT | D-67 | `createElements()` 的标签 / 元件注册先移植,错误用 `@ts-expect-error` | 04-11 | COVERED | Task 2 步骤 (1):`elements/index.ts` 逐字移动、注册体逻辑零改动;耦合由 R2 的 `@ts-expect-error` 覆盖;门禁断言注册调用点仍在 |
+| CONTEXT | D-68 | 任务只有移植:无「保证能运行」要求;架构耦合一律 `// @ts-expect-error` 暂记 | 04-11 | COVERED | 全部门禁为静态(路径 / 删除 / 桶 / 依赖 / 改指 / 反向引用 / 标注 / 允许差异 / 范围 / CRLF);prohibitions 禁「TS 诊断数 / `check:type` / `build`」类门禁 |
+| CONTEXT | D-33 | 范围外一律不改;不追求解决全部类型错误 | 04-11 | COVERED(边界) | `packages/` 与 `src/` 零改动;无任何「诊断归零」验收项 |
+| CONTEXT | D-42 / D-48 | `client-base/src/material/` 归用户、AI 不得改 | 04-11 | COVERED(禁用路径) | 禁用路径门禁断言 `material/` porcelain 为空;prohibitions 明列 |
+| CONTEXT | D-49 | 不考虑 legacy 兼容(因 legacy 插件面而生的约束作废) | 04-11 | COVERED(排除) | 无兼容性任务;外部包(`entry-client` / `entry-data` / `packages/legacy-ui`)零改动 |
+| CONTEXT | Deferred Ideas | 阶段 4 后续步骤(适配实施、双布局);`packages` 全部、`entry-client`、`legacy-plugin-*`、`data-fallback` | — | DEFERRED | 未写入任何任务;仅作为「零改动」的断言对象 |
+| LEDGER | A-06 / D-02 | 被移集合 → `client-modules/src/shared.ts` 4 处(最硬的反向依赖) | 04-11 | COVERED | D-58 的 R1 + 新建 `shared/`;门禁断言改指后的 4 处说明符与 `shared/shared.ts` 存在 |
+| LEDGER | A-04 / A-05 / D-03 / D-04 | 被移集合 → `render/renderer` 3 处 + `render/use.ts` 5 处 | 04-11 | COVERED | D-59 / D-68 的 R2 / R3 共 8 处 `@ts-expect-error`;零反向引用门禁 |
+| LEDGER | A-07 / D-06 / F-05 | `@user/client-base` 自包引用 5 处 | 04-11 | COVERED(只搬不改) | R4:说明符保持原文且不新增标注;由「允许差异门禁」证明未被改写;余留由用户后续裁定(assumptions) |
+| LEDGER | B-01..B-08 / C-01..C-08 | 外部导入点与 barrel 断链点 | 04-11 | COVERED | R8(实现层改指)+ R9(`render/utils` 标注)+ R6(目标桶)逐条对应;门禁按说明符逐一断言改指后的形态 |
+| LEDGER | D-07 | `client-base/package.json` 依赖缺口 | 04-11 | COVERED | R7:补 5 条(`@motajs/animate` / `@motajs/common` / `@motajs/render-vue` / `@motajs/system` / `@user/data-common`)+ `pnpm i` |
+| LEDGER | D-09 / F-07 | shader `?raw` 构建链 | 04-11 | COVERED(无动作) | D-64:不处理;R5 保证相对路径不变 |
+| LEDGER | E-01..E-06 | legacy 命中(仅报告;`core.*` 约 30 处 / `loading` + `Mota.require` 3 处 / `client.*` 0 命中) | 04-11 | COVERED(排除) | D-55 / D-65:随文件原样搬运,不修、不登记为待办;不新增任何 legacy 相关任务 |
+| LEDGER | F-01..F-10 | 未确定项 | 04-11 | COVERED | 逐条由 D-58..D-68 裁定;F-05 / F-10 的余留按本计划 assumptions 处置并在 Task 0 汇报、SUMMARY 登记 |
+
+## Estimate Basis(估算依据)
+
+`estimate-calibration` 返回 `factor: 1`、`applied: false`、`sample_count: 0`、`confidence: low`(项目无历史样本),故 `tokens == raw_tokens` 并如实标注 `confidence: low`。按「执行者实际触及量」估算(`estimate.tasks: 5` = Task 0 汇报关卡 + 4 个工作任务):Task 0 只读汇报并等待用户确认(约 6k,不写文件);Task 1(tracer)起始只读基线 + `layout/{layout,index}.ts` + `shared/{shared,index}.ts` + `client-base/src/index.ts` 的 2 条桶 + `package.json` 5 条依赖 + `pnpm i` + `render/utils/index.ts` 标注 + 删除 `render/utils/layout.ts` + 门禁(约 30k);Task 2 `components` + `elements` 18 文件移动 + 8 处标注 + 2 条桶 + `render/ui/` 8 文件 11 处 + `fallback/ui.ts` + `render/index.tsx` 改指 + 删除 18 原件 + 门禁(约 40k);Task 3 `map` 20 文件移动 + shared 说明符 4 处 + `client.ts` / `types.ts` / `render/index.tsx:45,46` 改指 + `render/utils/saves.ts` 标注 + 删除 20 原件 + 门禁(约 34k);Task 4 全量门禁 + 允许差异比对 + 人工复核 + `04-11-SUMMARY.md` + 提交(约 20k);固定开销约 5k;合计约 130k。**本计划刻意不做「多份 `*-PLAN.md`」拆分**:委托明确要求只产出 `04-11` 一份计划,故以任务切分替代计划切分,并保持每个任务落在单代理上下文预算内。
+
+
+
+@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-STRUCTURE-MIGRATION-IMPACT.md
+@.planning/phases/04-render-adaptation/04-10-PLAN.md
+@.planning/phases/04-render-adaptation/04-09-SUMMARY.md
+@.planning/phases/04-render-adaptation/04-PATTERNS.md
+@AGENTS.md
+@dev.md
+
+
+
+
+
+ Task 0: 预执行汇报关卡 —— 汇报渲染端结构性重构·第二步(移植实施)的问题、D-58..D-68 逐条裁定、移植即移动(MOVE)的语义与三处需当场确认的解读、39 文件源→目标映射、R1..R9 引用调整清单、范围与硬边界、验证方式,等待用户回复「可以执行」
+ 用户对渲染端结构性重构第二步的裁定 D-58..D-68 已写入 `.planning/phases/04-render-adaptation/04-CONTEXT.md`(以文件内实际存在 D-58..D-68 为准);第一步只读台账 `.planning/phases/04-render-adaptation/04-STRUCTURE-MIGRATION-IMPACT.md` 已在仓库中;移植集合在当日工作树上存在(`packages-user/client-modules/src/render/{components,elements,map}` 三个均为目录且分别含 13 / 5 / 20 个文件,`packages-user/client-modules/src/render/utils/layout.ts` 为文件);`packages-user/client-base/src/` 下**尚无** `components` / `elements` / `map` / `layout` / `shared` 目录(移植尚未发生)
+ (只读汇报,不修改任何文件)
+
+ - AGENTS.md(「任何计划的执行,其第一个 block 点必须是向我汇报计划内容并等待我确认」;只有明确回复「可以执行」四个字之后才允许开始执行;「任何公共、受保护、私有方法、字段等内容的命名必须先向我反馈」;「不得擅自修改所有 jsDoc 注释,不得擅自修改任何已有注释」)
+ - .planning/phases/04-render-adaptation/04-CONTEXT.md(D-51..D-57 第一步裁定;**D-58..D-68 第二步逐条裁定原文**;D-33 范围;D-42 / D-48 `material/` 禁用;D-49 legacy 兼容作废;D-55 / D-65 legacy 不管)
+ - .planning/phases/04-render-adaptation/04-STRUCTURE-MIGRATION-IMPACT.md(第一步台账:A9 / B9 / C8 / D10 / E6 / F10 的 `file:line` 锚点,本计划的输入)
+ - .planning/phases/04-render-adaptation/04-11-PLAN.md(本计划全文:objective 的源→目标映射表与 R1..R9 引用调整清单、tasks、must_haves、禁令、门禁)
+ - .planning/phases/04-render-adaptation/04-10-PLAN.md:169-249(上一增量 Task 0 汇报关卡的结构与汇报口径,本关卡对齐版式)
+ - .planning/phases/04-render-adaptation/04-09-SUMMARY.md:56-131(上一增量(含代码改动)的落点表 / 门禁摘要 / 人工复核 / 无新命名声明 / 运行时不可得声明的写法)
+ - dev.md(「项目结构」单向依赖 `src → packages-user → packages`;「双端分离」`@user/client-base` 系统层 / `@user/client-modules` 实现层;「类型规范」的 `// @ts-expect-error` 须说明原因;「其他要求」的 CRLF)
+ - packages-user/client-modules/src/action/move.ts:1-6 与 packages-user/client-modules/src/render/ui/statistics.tsx:1-12(**既有先例**:`// @ts-expect-error <原因>` 写在 import 块**中间**的 import 行之上,故 R2 / R3 的标注写在本仓库属既有实践)
+
+
+ **停下并向用户汇报,等待明确回复后才继续;汇报本身只读,不写任何文件、不改任何生产代码、不派发子代理。**
+
+ ① **要解决的问题(D-51 尚未落地)**:04-10 已产出只读影响台账 `04-STRUCTURE-MIGRATION-IMPACT.md`(A9 / B9 / C8 / D10 / E6 / F10),确认 `packages-user/client-modules/src/render/` 下的通用渲染内容(`components` / `elements` / `map` 三文件夹 + `render/utils/layout.ts`,共 **39** 文件)本属渲染**系统层** `packages-user/client-base/src/`。本步按用户裁定 D-58..D-68 **执行移植**,完成分层纠正;台账 F 类的 10 条未决项已全部裁定。
+
+ ② **锁定裁定(须逐条汇报 D-58..D-68 原文要点)**:
+ - **D-58(shared)**:系统层新建 `client-base/src/shared/`,把被移 `map` 文件实际需要的常量挪过去;`client-modules/src/shared.ts` **保留**给留在实现层的消费者。
+ - **D-59(renderer / using)**:`render/renderer`(`mainRenderer` / `tagManager` / `using`)涉及整体架构,**不处理、不做反向引用、不做解耦**;被移文件中相应导入 / 使用处以 `// @ts-expect-error`(附简要原因)暂记。
+ - **D-60(utils)**:`render/utils/` **不移植**;其指向被移内容(`layout.ts` 及其他)的引用以 `// @ts-expect-error` 处理。
+ - **D-61(layout)**:`render/utils/layout.ts` → `client-base/src/layout/layout.ts`;并**新增 `client-base/src/layout/index.ts`**(桶)。
+ - **D-62(命名)**:文件名 / 目录名**与原先一致**;操作 = 直接复制文件,然后调整引用。
+ - **D-63(桶导出与消费者)**:`client-base/src/index.ts` **新增** `./components` / `./elements` / `./map` / `./layout` / `./shared` 的桶导出;实现层消费者改为**直接引用 `@user/client-base`**。
+ - **D-64(`?raw`)**:shader 的 `?raw` 引用可正常执行,**不需要处理**。
+ - **D-65(legacy)**:任何情况下都不考虑 legacy(重申 D-55)——legacy 报错一律不管。
+ - **D-66(依赖)**:`client-base/package.json` **新增所需依赖并执行 `pnpm i`**。
+ - **D-67(标签注册)**:`createElements()` 的标签 / 元件注册**先移植,错误用 `@ts-expect-error`**。
+ - **D-68(本步性质)**:任务只有**移植**——把那几个文件夹复制到系统层并调整引用;**没有「保证能运行」的要求**,其余什么都不用管;架构耦合 / 无法立刻正确接线处一律 `// @ts-expect-error` 暂记;**效率优先,细节不纠结**。
+
+ ③ **需用户当场确认或否决的三处解读(本计划的判断,须原样呈报)**:
+ - **(i) 「移植」按 MOVE 落地**:把 39 个文件按 D-53 / D-61 目标布局放置到 `client-base/src/` 下,**并删除** `client-modules/src/render/` 下的原件(`components` / `elements` / `map` 三目录清空、`render/utils/layout.ts` 删除)。理由:只复制不删除会留下同名死副本,D-63 的「实现层消费者改指 `@user/client-base`」将失去意义并留下两套真相。**若用户意图是 copy-and-leave,此否决点即可拦下**,本计划退出、`files_deleted` 清空后重新汇报。
+ - **(ii) `render/use.ts` 方向按 D-68 兜底**:D-59 只点名 `render/renderer`,D-60 只点名 `render/utils/`。`components/{choices,input,misc,scroll,tip}.tsx` 对 `render/use.ts` 的 `useKey` / `transitioned` / `transitionedColor` 共 5 处 import 与 `render/renderer` 属同一性质(留在实现层的模块被系统层反向引用),故按 D-59 的原则 + D-68「架构耦合 / 无法立刻正确接线处一律 `// @ts-expect-error` 暂记」同样处理。**用户若认为该方向应另案处理,此否决点即可拦下。**
+ - **(iii) `client-base/src/shared/` 的文件形态**:采用 `shared/shared.ts`(10 个常量)+ `shared/index.ts`(`export * from './shared'`)两文件,以对齐 D-61 对 `layout/` 给出的「内容文件 + 桶」先例,并满足 D-63 对 `./shared` 的桶导出。**用户若希望改为单文件 `shared/index.ts`,此否决点即可拦下。**
+
+ ④ **源 → 目标映射(39 文件,须原样呈报)**:`client-modules/src/render/components/`(13)→ `client-base/src/components/`;`render/elements/`(5)→ `client-base/src/elements/`;`render/map/`(20,含 `extension/` 6 + `shader/` 4)→ `client-base/src/map/`;`render/utils/layout.ts`(1)→ `client-base/src/layout/layout.ts` + 新增 `layout/index.ts`;另新增 `client-base/src/shared/{shared,index}.ts`。**不新建 `client-base/src/render/` 目录。**
+
+ ⑤ **R1..R9 引用调整清单(唯一允许的改动面,须原样呈报)**:**R1** 被移 `map` 文件的 shared 引用改指(`map/{element,renderer,vertex}.ts` 的 `'../../shared'` → `'../shared'`;`map/extension/door.ts` 的 `'../../../shared'` → `'../../shared'`);**R2** 3 处 `'../renderer'` import(`elements/index.ts`、`components/{misc.tsx,textboxTyper.ts}`)文本不变 + 上一行 `// @ts-expect-error <原因>`;**R3** 5 处 `'../use'` import(`components/{choices,input,misc,scroll,tip}.tsx`)同上;**R4** 5 处 `@user/client-base` 自包引用**只搬不改、不加标注**;**R5** 被移集合内部相对引用与 4 条 `?raw` 相对路径**不变**(`map/shader/*`);**R6** `client-base/src/index.ts` 新增 5 条桶导出、保留原有 4 行;**R7** `client-base/package.json` 新增 5 条 `workspace:*` 依赖 + 根执行 `pnpm i`;**R8** 实现层消费者 14 个文件的说明符改指 `'@user/client-base'`(`render/ui/` 8 文件 11 处、`fallback/ui.ts:2`、`render/index.tsx:5,45,46`、`client-modules/src/client.ts:25`、`client-modules/src/types.ts:2`);**R9** `render/utils/index.ts:1` 与 `render/utils/saves.ts:2` 文本不变 + 上一行 `// @ts-expect-error <原因>`。
+
+ ⑥ **新增依赖(须原样呈报)**:`@motajs/animate` / `@motajs/common` / `@motajs/render-vue` / `@motajs/system` / `@user/data-common`(均 `workspace:*`,对齐 `client-modules/package.json` 的声明风格);根级已声明的第三方包(`vue` / `lodash-es` / `eventemitter3` / `mutate-animate`)按项目既有惯例**不**在包内重复声明。
+
+ ⑦ **范围与硬边界(须原样呈报)**:**零触碰** —— `client-base/src/material/**`(D-42 / D-48)、`client-base/src/load/**`、`client-modules/src/{action,fx,weather,scene,renderer.ts,use.ts,index.tsx}` 的实现(`render/index.tsx` 仅允许说明符改指)、`packages/**`、`src/**`、其它 `package.json`、任何测试文件、legacy 面(D-55 / D-65)、用户并发改动(D-56)。**红线**:`client-base` 不得反向依赖 `client-modules`(不得出现 `from '@user/client-modules'`)。**命名**:全部文件名 / 目录名与原先一致(D-62),不重命名、不改大小写、不改扩展名——即本步**不引入任何新命名**(AGENTS.md 的命名前置要求不适用)。
+
+ ⑧ **验证方式(须原样呈报,并声明本步无运行时要求)**:D-68 明示本步**没有「保证能运行」的要求**。证明方式全部为**静态**:① **目标落位门禁**:42 个新增文件路径存在;② **移动已发生门禁**:`files_deleted` 的 39 条逐条不存在、`render/{components,elements,map}` 三目录内零文件、`client-base/src/render/` 不存在;③ **桶导出门禁**:`client-base/src/index.ts` 含 5 条新说明符且原 4 行保留;④ **依赖与 lockfile 门禁**:`client-base/package.json` 含 5 条新依赖、原 5 条保留、`pnpm-lock.yaml` 的 `packages-user/client-base` importer 块同步;⑤ **消费者改指门禁**:各说明符计数逐一符合预期(`'./render/map'` 0、`'./components'` 0、`'./elements'` 0、`'../components'` 1(`render/utils/saves.ts`)、`'../components/icons'` 0、`'../components/misc'` 0、`'../render/components/tip'` 0、`'./layout'` 1(`render/utils/index.ts`));⑥ **反向引用零命中门禁**:`git grep -n -F "@user/client-modules" -- packages-user/client-base` 与 `git grep -n -F "client-modules" -- packages-user/client-base` 均零命中;⑦ **`@ts-expect-error` 落点门禁**:授权 10 处新增(R2 3 + R3 5 + R9 2)且每行均带非空原因,被移文件内既有 7 条逐字保留;⑧ **允许差异门禁**:39 个被移文件与原件逐行比对,差异**只**允许出现在 R1 / R2 / R3 三面;`layout.ts` 须逐字相同;`shared/shared.ts` 须为 `client-modules/src/shared.ts` 的逐行摘取;⑨ **范围与用户改动门禁**:`material/` / `load/` / `packages/` / `src/` porcelain 为空、`04-CONTEXT.md` 未提交改动仍在、porcelain 基线除计划内条目外零漂移;⑩ **CRLF 门禁**;⑪ **人工复核**。**不得使用** `pnpm check:type` / `pnpm build` / 全仓(或范围内)TS 诊断数作为门禁或验收项。
+
+ ⑬ **编号与 wave(须原样告知)**:本计划为 **04-11**(第十一个增量),`wave: 10`,`depends_on` 列 `04-01`..`04-10`;phase 目录内既有 `04-01`..`04-10` 的 `*-PLAN.md` 与 `*-SUMMARY.md` **一律不得改写 / 覆盖 / 改号**,也不得产出 `04-12+`。
+
+ ⑭ **盖章效力(须原样告知)**:本计划为**代码改动型**增量,将实际移动 39 个文件、改 14 个实现层文件的引用行、改 1 个 `client-base` 的 `index.ts` 与 `package.json`、执行 1 次 `pnpm i`,并删除 `client-modules/src/render/` 下 39 个原件。所有改动均可用 `git revert` 整体回退。
+
+ ⑮ **关卡性质(必须原样告知用户)**:无论触发方式(`/gsd-execute-phase`、`/gsd-manager`、`/gsd-autonomous`、子代理/执行器派发)或配置模式(`.planning/config.json` 的 `mode: yolo`、`workflow.auto_advance`)如何,**执行者/子代理都必须在此停下并等待**用户明确回复「可以执行」四个字;未收到这四个字前不得执行任何任务、不得派发执行子代理,也不得以「自动化模式」为由自动放行。
+
+ ⑯ **失败语义(等同硬门禁,非自动命令)**:用户未回复「可以执行」即继续,或回复的是其它任何内容(含修订意见、沉默、`yolo` / `auto_advance` 模式下的自动推进、或子代理/执行器的自行放行),一律视为**未通过本关卡**:必须立即停止执行,不派发任何执行子代理,保持只读、不写任何文件,直到用户在对话中明确回复「可以执行」四个字后方可继续。本条只以本 `` 与 `` 表达,不写成 XML 失败声明元素(人工关卡没有可运行的自动命令可与之绑定)。
+
+ ⑰ **若用户给出修订意见**(含否决搬运语义 / 否决 `render/use.ts` 的兜底 / 否决 `shared/` 的两文件形态 / 不同意依赖清单 / 希望保留原件 / 希望调整范围):退出本计划、修订 `04-11-PLAN.md` 后重新汇报,不得自行改道执行。
+
+ 是否按本计划(渲染端结构性重构·第二步移植实施:把 39 个文件从 `client-modules/src/render/` 移入 `client-base/src/{components,elements,map,layout}/` 并删除原件、新建 `client-base/src/shared/`、`client-base/src/index.ts` 新增 5 条桶导出、14 个实现层消费者改指 `@user/client-base`、`client-base/package.json` 补 5 条依赖并执行 `pnpm i`、架构耦合处共 10 处 `// @ts-expect-error`(附原因)暂记、命名一律不变、`material/` 与 legacy 与 `packages/` 与 `src/` 与用户改动零触碰、无运行时要求)开始执行
+
+ AGENTS.md 要求任何计划的第一个 block 点必须是向用户汇报计划内容并等待用户确认。`.planning/config.json` 为 `mode: yolo`,因此该关卡必须显式声明为 `blocking-human` 并置 `autonomous: false`,否则计划层面不提供任何 block 点。本次存在三处会改变交付物形态的判断(搬运语义 MOVE / `render/use.ts` 方向的兜底 / `shared/` 的文件形态)必须在动手前澄清,否则可能移错、漏移或留下死副本;另有一条会放宽验收的裁定(D-68:不要求可运行、不要求零类型错误)必须让用户明确知晓,避免执行者事后用「类型不干净」为由扩大范围或阻塞交付。
+
+
+
+
+
+
+ 回复「可以执行」四个字(修订意见请按 action ⑰ 处理)
+
+ 用户在对话中明确回复「可以执行」四个字(人工核对用户回复原文;本关卡为人工关卡,无自动命令,失败语义写在 Task 0 的 action ⑯ 与 acceptance_criteria 中)
+
+
+ - 用户回复中明确包含「可以执行」四个字
+ - 汇报内容涵盖:要解决的问题(D-51 分层放错尚未落地 + 本步为第二步实施)、锁定裁定 D-58..D-68 逐条要点、**移植按 MOVE 的语义与 (i)(ii)(iii) 三处可否决解读**、39 文件源→目标映射、R1..R9 引用调整清单、新增依赖 5 条、范围与硬边界(`material/` / legacy / `packages/` / `src/` / 用户改动零触碰 + 反向引用红线)、验证方式
+ - 汇报中已原样告知 **D-68**(没有「保证能运行」的要求;架构耦合一律 `@ts-expect-error` 暂记)与 **D-62**(命名一律不变、本步不引入任何新命名)
+ - 汇报中已原样告知 **编号与 wave**(04-11 / wave 10 / `depends_on` 04-01..04-10)以及既有 `04-01`..`04-10` 产物不得改写 / 覆盖 / 改号、不得产出 `04-12+`
+ - 汇报中已原样告知**验证方式**(目标落位 / 移动已发生 / 桶导出 / 依赖与 lockfile / 消费者改指 / 反向引用零命中 / `@ts-expect-error` 落点 / 允许差异 / 范围与用户改动 / CRLF / 人工复核),并声明**不使用** `pnpm check:type` / `pnpm build` / TS 诊断数类门禁
+ - 汇报中已原样告知**关卡性质**:执行者/子代理必须停下等待,不因触发方式或 `yolo` / `auto_advance` 模式自动放行
+
+ 用户明确回复「可以执行」;在此之前未执行任何任务、未修改任何文件、未派发任何执行子代理
+
+
+
+ Task 1(tracer):端到端一条线走通全部层 —— 起始只读基线 + `layout` 切片(`layout/layout.ts` + 新 `layout/index.ts`)+ 新 `shared/`(10 常量 + 桶)+ `client-base/src/index.ts` 两条桶 + `package.json` 5 条依赖 + 根 `pnpm i` + `render/utils/index.ts` 的 `@ts-expect-error` + 删除 `render/utils/layout.ts`;目标落位 / 移动已发生 / 桶 / 依赖与 lockfile / 标注 / 反向引用 / 允许差异 / 范围 / CRLF 九道门禁转绿
+ Task 0 已闭环:用户在对话中明确回复「可以执行」四个字(未收到则不得开始本任务)
+ packages-user/client-base/src/layout/layout.ts, packages-user/client-base/src/layout/index.ts, packages-user/client-base/src/shared/shared.ts, packages-user/client-base/src/shared/index.ts, packages-user/client-base/src/index.ts, packages-user/client-base/package.json, packages-user/client-modules/src/render/utils/index.ts, packages-user/client-modules/src/render/utils/layout.ts(删除), pnpm-lock.yaml
+
+ - packages-user/client-modules/src/render/utils/layout.ts(整文件 86 行;唯一 import 为 `:1` 的 `import { ElementLocator } from '@motajs/render';`;无被移集合内部引用、无 legacy 命中——即目标副本应逐字相同)
+ - packages-user/client-modules/src/shared.ts(整文件;摘取常量与其 jsDoc / `//#region 地图` 注释的**原文**;被移 `map` 文件实际需要的十个常量见 action 步骤 (2))
+ - packages-user/client-modules/src/render/map/element.ts:5 与 packages-user/client-modules/src/render/map/extension/door.ts:9 与 packages-user/client-modules/src/render/map/renderer.ts:44-53 与 packages-user/client-modules/src/render/map/vertex.ts:19-25(确认「被移 `map` 文件实际需要的常量」集合与其传递依赖,供步骤 (2) 摘取)
+ - packages-user/client-base/src/index.ts(现行 5 行:`./load` / `./material` / `./save` + 空行 + `./types`;本次在其后追加,不得改写原有行)
+ - packages-user/client-base/package.json(现行 10 行:`name` + `dependencies` 5 条;本次追加 5 条,不得改写原有行)
+ - packages-user/client-modules/package.json(声明风格基准:`"@motajs/animate": "workspace:*"` 等)
+ - packages-user/client-modules/src/render/utils/index.ts(现行 3 行 `export * from './layout' | './saves' | './use'`;`:1` 为 D-60 的标注点)
+ - packages-user/client-modules/src/action/move.ts:1-6 与 packages-user/client-modules/src/render/ui/statistics.tsx:1-12(`// @ts-expect-error <原因>` 与 import 行的既有书写先例)
+ - dev.md(「模块原则」的无类型导入 / 引入位置;「类型规范」的 `// @ts-expect-error` 须说明原因;「其他要求」的 CRLF)
+
+
+ (0a) **起始基线快照(只读前置,先做这一步)**:执行 `git status --porcelain -- packages packages-user src`,把输出**原样逐行**记为「只读起始基线」记入本任务的执行记录(并写入 `04-11-SUMMARY.md` 的对应栏):先写 bullet `- **只读起始基线(packages / packages-user / src)**:`,其后逐行抄录输出(每条一行,行首为两字符状态码;输出为空则写 `(无)`)。规划日实测为**空**(2026-09-24;`.planning/phases/04-render-adaptation/04-CONTEXT.md` 有未提交改动,但该路径不在本集合内故不计入 porcelain 基线)。同时记下 `git rev-parse HEAD` 与 `git rev-parse --abbrev-ref HEAD` 的当日值写入记录(规划日为 `refine/data-client` 分支)。**不得回滚、暂存、提交或修改**任何既有改动。
+
+ (1) **建 `layout` 切片(D-61 + D-62)**:把 `packages-user/client-modules/src/render/utils/layout.ts` **逐字**复制为 `packages-user/client-base/src/layout/layout.ts`(不得增删任何行、不得改注释、不得改 `:1` 的 `import { ElementLocator } from '@motajs/render';`);新建 `packages-user/client-base/src/layout/index.ts`,内容为单行 `export * from './layout';`(桶,D-61)。**不得**新建 `packages-user/client-base/src/render/` 目录。
+
+ (2) **建 `shared` 切片(D-58)**:新建 `packages-user/client-base/src/shared/shared.ts`,从 `packages-user/client-modules/src/shared.ts` 中摘取被移 `map` 文件实际需要的常量(含传递依赖):`CELL_WIDTH`、`CELL_HEIGHT`、`CELL_SIZE`、`MAP_BLOCK_WIDTH`、`MAP_BLOCK_HEIGHT`、`MAP_WIDTH`、`MAP_HEIGHT`、`DYNAMIC_RESERVE`、`MOVING_TOLERANCE`、`DOOR_ANIMATE_INTERVAL`(共 **10** 个;`MAP_WIDTH` / `MAP_HEIGHT` 依赖 `CELL_SIZE` 与 `MAP_BLOCK_*`,必须一并带上以保持文件自洽)。**逐行原文摘取**:常量声明行与其紧邻的 jsDoc / 单行注释(如 `CELL_WIDTH` / `MAP_WIDTH` / `DYNAMIC_RESERVE` / `MOVING_TOLERANCE` / `DOOR_ANIMATE_INTERVAL` 的注释)必须与原件**逐字一致**;按需保留 `//#region 地图` / `//#endregion` 边界行;该文件的 import 区只需 `import { ElementLocator, Font } from '@motajs/render';`(若摘取后的常量不再用到某符号,为遵守 dev.md「不得修改既有注释」与「最小改动」,**保留原 import 行原文**)。新建 `packages-user/client-base/src/shared/index.ts`,内容为单行 `export * from './shared';`。**不得**改动 `packages-user/client-modules/src/shared.ts`(D-58 明示保留给实现层消费者)。
+
+ (3) **目标侧桶导出(D-63 的第一部分)**:编辑 `packages-user/client-base/src/index.ts`,在**保留**既有 5 行原文(`export * from './load';` / `'./material'` / `'./save'` / 空行 / `'./types'`)的前提上追加两行:`export * from './layout';` 与 `export * from './shared';`(其余三条 `./components` / `./elements` / `./map` 由 Task 2 / Task 3 追加)。
+
+ (4) **依赖与安装(D-66)**:编辑 `packages-user/client-base/package.json`,在 `dependencies` 中**追加** 5 条并保持既有 5 条不变:`"@motajs/animate": "workspace:*"`、`"@motajs/common": "workspace:*"`、`"@motajs/render-vue": "workspace:*"`、`"@motajs/system": "workspace:*"`、`"@user/data-common": "workspace:*"`(键序对齐 `client-modules/package.json` 的现有书写风格即可;**不得**把根级已声明的 `vue` / `lodash-es` / `eventemitter3` / `mutate-animate` 写进来)。随后在**仓库根**执行 `pnpm i` 并确认退出码为 0、`pnpm-lock.yaml` 的 `packages-user/client-base` importer 块已同步出现上述 5 条。**不得**改动任何其它 `package.json`。
+
+ (5) **消费者标注(D-60)**:编辑 `packages-user/client-modules/src/render/utils/index.ts`,在 `:1` 的 `export * from './layout';` **上一行**插入 `// @ts-expect-error <简要原因>`(原因须写明「`layout.ts` 已按 D-58..D-68 移植到 `@user/client-base`,本桶按 D-60 暂以标注记录断链」这类可读说明);**其余两行 `'./saves'` / `'./use'` 原文不得改动**,也**不得**把该文件改为引用 `@user/client-base`。
+
+ (6) **删除原件(MOVE 语义)**:删除 `packages-user/client-modules/src/render/utils/layout.ts`。删除后 `packages-user/client-modules/src/render/utils/` 下应仅剩 `index.ts` / `saves.ts` / `use.ts`。
+
+ (7) **CRLF 与收口**:确认本任务创建 / 修改的全部文件(含 `pnpm-lock.yaml`)为 CRLF 换行(`dev.md`);本任务只允许改 (1)..(6) 列出的文件,**不得**动任何其它文件、不得新增计划文件、不得派发会写文件的子代理。
+
+ 本任务是纯结构迁移的第一条切片(新建两个目录 + 两条桶 + 五条依赖 + 一处标注 + 删除一个已被复制的原件),全部改动都在 git 追踪范围内,可用 `git revert` 或重跑本计划整体回退;未引入任何公共接口行为、契约或新命名。
+
+
+ node -e "const fs=require('fs');const f=['packages-user/client-base/src/layout/layout.ts','packages-user/client-base/src/layout/index.ts','packages-user/client-base/src/shared/shared.ts','packages-user/client-base/src/shared/index.ts'];const miss=f.filter(x=>!fs.existsSync(x));if(miss.length>0){console.error('TRACER PATHS MISSING '+miss.length+' :: '+miss.join(' ; '));process.exit(1)}if(fs.existsSync('packages-user/client-base/src/render')){console.error('FORBIDDEN client-base/src/render DIR');process.exit(1)}console.log('OK tracer new paths '+f.length)"
+ non-zero exit, 或 stderr 含 "TRACER PATHS MISSING" / "FORBIDDEN"(`layout/{layout,index}.ts` 或 `shared/{shared,index}.ts` 未落位,或违规新建了 `client-base/src/render/` 目录)
+ node -e "const fs=require('fs');const gone=['packages-user/client-modules/src/render/utils/layout.ts'];const bad=gone.filter(x=>fs.existsSync(x));if(bad.length>0){console.error('ORIGINAL NOT DELETED :: '+bad.join(' ; '));process.exit(1)}if(!fs.existsSync('packages-user/client-modules/src/shared.ts')){console.error('SHARED.TS MUST BE KEPT (D-58)');process.exit(1)}console.log('OK layout.ts deleted, shared.ts kept')"
+ non-zero exit, 或 stderr 含 "ORIGINAL NOT DELETED" / "SHARED.TS MUST BE KEPT"(`render/utils/layout.ts` 未被删除,或误删了 `client-modules/src/shared.ts` —— 违反 MOVE 语义或 D-58)
+ node -e "const fs=require('fs');const t=fs.readFileSync('packages-user/client-base/src/index.ts','utf8');const need=['./load','./material','./save','./types','./layout','./shared'];const miss=need.filter(s=>t.indexOf('export * from '+String.fromCharCode(39)+s+String.fromCharCode(39))<0);if(miss.length>0){console.error('BARREL MISSING '+miss.join(','));process.exit(1)}console.log('OK client-base barrel (tracer 2 of 5 added)')"
+ non-zero exit, 或 stderr 含 "BARREL MISSING"(`client-base/src/index.ts` 未追加 `./layout` / `./shared`,或原有 `./load` / `./material` / `./save` / `./types` 被改写)
+ node -e "const fs=require('fs');const pkg=JSON.parse(fs.readFileSync('packages-user/client-base/package.json','utf8'));const d=pkg.dependencies||{};const need=['@motajs/animate','@motajs/common','@motajs/render-vue','@motajs/system','@user/data-common'];const keep=['@motajs/audio','@motajs/render','@motajs/client-base','@user/data-base','@user/data-state'];const miss=need.filter(x=>!d[x]);const lost=keep.filter(x=>!d[x]);if(miss.length+lost.length>0){console.error('PKG DEPS bad missing=['+miss.join(',')+'] removed=['+lost.join(',')+']');process.exit(1)}const lock=fs.readFileSync('pnpm-lock.yaml','utf8');const m=lock.match(/\n packages-user\/client-base:\r?\n/);if(!m){console.error('LOCK IMPORTER BLOCK MISSING');process.exit(1)}const rest=lock.slice(m.index+m[0].length);const end=rest.search(/\r?\n \S/);const block=end<0?rest:rest.slice(0,end);const unsynced=need.filter(x=>block.indexOf(x+':')<0);if(unsynced.length>0){console.error('LOCK UNSYNCED '+unsynced.join(','));process.exit(1)}console.log('OK client-base deps + lockfile importer synced')"
+ non-zero exit, 或 stderr 含 "PKG DEPS" / "LOCK IMPORTER BLOCK MISSING" / "LOCK UNSYNCED"(`client-base/package.json` 未补 5 条依赖或误删原有依赖,或 `pnpm i` 未执行 / 未同步 lockfile —— D-66 未落地)
+ node -e "const fs=require('fs');const t=fs.readFileSync('packages-user/client-modules/src/render/utils/index.ts','utf8').replace(/\r\n/g,'\n');const lines=t.split('\n');const i=lines.findIndex(l=>l.indexOf('export * from '+String.fromCharCode(39)+'./layout'+String.fromCharCode(39))>=0);if(i<1){console.error('LAYOUT EXPORT LINE NOT FOUND OR AT TOP');process.exit(1)}if(lines[i-1].indexOf('@ts-expect-error')<0){console.error('MISSING @ts-expect-error ABOVE layout export (D-60)');process.exit(1)}if(/@ts-expect-error\s*$/.test(lines[i-1])){console.error('BARE @ts-expect-error WITHOUT REASON');process.exit(1)}if(t.indexOf(String.fromCharCode(39)+'./saves'+String.fromCharCode(39))<0||t.indexOf(String.fromCharCode(39)+'./use'+String.fromCharCode(39))<0){console.error('SAVES/USE EXPORT LINES MODIFIED');process.exit(1)}console.log('OK D-60 annotation on render/utils/index.ts')"
+ non-zero exit, 或 stderr 含 "LAYOUT EXPORT LINE NOT FOUND" / "MISSING @ts-expect-error" / "BARE @ts-expect-error" / "SAVES/USE EXPORT LINES MODIFIED"(`render/utils/index.ts:1` 未按 D-60 加带原因的标注,或标注无原因,或另两行被改动)
+ node -e "const{execSync}=require('child_process'),fs=require('fs');const head=execSync('git rev-parse HEAD',{encoding:'utf8'}).trim();const orig=execSync('git show '+head+':packages-user/client-modules/src/render/utils/layout.ts',{encoding:'utf8',maxBuffer:1e8});const now=fs.readFileSync('packages-user/client-base/src/layout/layout.ts','utf8');if(orig.replace(/\r\n/g,'\n')!==now.replace(/\r\n/g,'\n')){console.error('LAYOUT COPY NOT VERBATIM (D-62)');process.exit(1)}const src=fs.readFileSync('packages-user/client-modules/src/shared.ts','utf8').replace(/\r\n/g,'\n');const sh=fs.readFileSync('packages-user/client-base/src/shared/shared.ts','utf8').replace(/\r\n/g,'\n');const bad=sh.split('\n').filter(l=>l.trim().length>0).filter(l=>src.indexOf(l)<0);if(bad.length>0){console.error('SHARED EXTRACTION INTRODUCED NEW LINES '+bad.length+' :: '+bad.slice(0,3).join(' ; '));process.exit(1)}console.log('OK layout verbatim + shared pure extraction')"
+ non-zero exit, 或 stderr 含 "LAYOUT COPY NOT VERBATIM" / "SHARED EXTRACTION INTRODUCED NEW LINES"(`layout/layout.ts` 与原件不逐字相同,或 `shared/shared.ts` 出现原件中不存在的非空行 —— 违反 D-62 的「直接复制」与「不得修改既有注释」)
+ node -e "const{execSync}=require('child_process');let hits=[];for(const pat of ['@user/client-modules','client-modules']){let o='',st=0;try{o=execSync('git grep -n -F \"'+pat+'\" -- packages-user/client-base',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'');st=e.status}if(o.replace(/\s+$/,'').length>0)hits.push(pat+' => '+o.split(/\r?\n/).slice(0,3).join(' ; '));else if(st!==0&&st!==1)hits.push(pat+' grep status '+st)}if(hits.length>0){console.error('REVERSE DEPENDENCY DETECTED: '+hits.join(' || '));process.exit(1)}console.log('OK no reverse dependency into client-modules')"
+ non-zero exit, 或 stderr 含 "REVERSE DEPENDENCY DETECTED"(`client-base` 内出现对 `client-modules` 的引用 —— 分层红线被破,或 D-59 的「不做反向引用」被违反)
+ node -e "const{execSync}=require('child_process');const o=execSync('git status --porcelain -- packages-user/client-base/src/material packages-user/client-base/src/load packages packages/ src',{encoding:'utf8'}).replace(/\s+$/,'');if(o.length>0){console.error('FORBIDDEN PATH TOUCHED: '+o);process.exit(1)}const c=execSync('git status --porcelain -- .planning/phases/04-render-adaptation/04-CONTEXT.md',{encoding:'utf8'}).replace(/\s+$/,'');const req=execSync('git status --porcelain -- .planning/REQUIREMENTS.md',{encoding:'utf8'}).replace(/\s+$/,'');if(req.length>0){console.error('READ-ONLY VIOLATION .planning/REQUIREMENTS.md');process.exit(1)}console.log('OK forbidden paths clean; CONTEXT dirty='+String(c.length>0))"
+ non-zero exit, 或 stderr 含 "FORBIDDEN PATH TOUCHED" / "READ-ONLY VIOLATION"(`material/` / `load/` / `packages/` / `src/` 被改动,或 `.planning/REQUIREMENTS.md` 被改动 —— D-42 / D-48 / D-33 边界被破)
+ node -e "const fs=require('fs');const files=['packages-user/client-base/src/layout/layout.ts','packages-user/client-base/src/layout/index.ts','packages-user/client-base/src/shared/shared.ts','packages-user/client-base/src/shared/index.ts','packages-user/client-base/src/index.ts','packages-user/client-base/package.json','packages-user/client-modules/src/render/utils/index.ts','pnpm-lock.yaml'];const bad=[];for(const f of files){const s=fs.readFileSync(f,'utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf)bad.push(f+' ('+(lf-crlf)+' stray LF)')}if(bad.length>0){console.error('NON-CRLF '+bad.join(' ; '));process.exit(1)}console.log('OK CRLF '+files.length+' files')"
+ non-zero exit, 或 stderr 含 "NON-CRLF"(本任务创建 / 修改的文件出现非 CRLF 换行,违反 dev.md「换行使用 CRLF 格式」)
+
+
+ - `packages-user/client-base/src/layout/layout.ts` 为 `packages-user/client-modules/src/render/utils/layout.ts` 的**逐字副本**(与 `git show :原路径` 逐行一致);`packages-user/client-base/src/layout/index.ts` 为 `export * from './layout';`
+ - `packages-user/client-base/src/shared/shared.ts` 含 10 个被移 `map` 文件所需常量(`CELL_WIDTH` / `CELL_HEIGHT` / `CELL_SIZE` / `MAP_BLOCK_WIDTH` / `MAP_BLOCK_HEIGHT` / `MAP_WIDTH` / `MAP_HEIGHT` / `DYNAMIC_RESERVE` / `MOVING_TOLERANCE` / `DOOR_ANIMATE_INTERVAL`),且**每一非空行都逐字出现在 `packages-user/client-modules/src/shared.ts` 中**(纯摘取、注释零改写);`shared/index.ts` 为 `export * from './shared';`
+ - `packages-user/client-modules/src/shared.ts` **保留未动**(D-58)
+ - `packages-user/client-base/src/index.ts` 追加 `./layout` 与 `./shared` 两条桶导出,原有 4 条 `./load` / `./material` / `./save` / `./types` 原文保留
+ - `packages-user/client-base/package.json` 追加 5 条 `workspace:*` 依赖(`@motajs/animate` / `@motajs/common` / `@motajs/render-vue` / `@motajs/system` / `@user/data-common`),原 5 条保留;仓库根 `pnpm i` 已执行且 `pnpm-lock.yaml` 的 `packages-user/client-base` importer 块同步
+ - `packages-user/client-modules/src/render/utils/index.ts` 的 `export * from './layout';` 上一行新增带原因的 `// @ts-expect-error`(D-60),另两行原文保留
+ - `packages-user/client-modules/src/render/utils/layout.ts` 已删除;`packages-user/client-base/src/render/` 不存在
+ - `git grep -n -F "@user/client-modules" -- packages-user/client-base` 与 `git grep -n -F "client-modules" -- packages-user/client-base` 均零命中
+ - `material/` / `load/` / `packages/` / `src/` 零改动;`.planning/REQUIREMENTS.md` 零改动;用户的 `04-CONTEXT.md` 未提交改动仍在
+ - 本任务触及的文件全部 CRLF;未触碰任何其它文件;未新增计划文件;未派发会写文件的子代理
+
+ 端到端一条线走通全部层:`layout` 切片(`layout/layout.ts` 逐字副本 + 新 `layout/index.ts`)+ 新 `shared/`(10 常量纯摘取 + 桶)+ `client-base/src/index.ts` 两条桶 + `client-base/package.json` 5 条依赖与 lockfile 同步 + `render/utils/index.ts` 的 D-60 标注 + `render/utils/layout.ts` 已删除;九道门禁(目标落位 / 移动已发生 / 桶 / 依赖与 lockfile / 标注 / 反向引用 / 允许差异 / 范围 / CRLF)全绿;后续任务只需按同一形态横向铺开 `components` / `elements` / `map`
+
+
+
+ Task 2:`components`(13)+ `elements`(5)两个文件夹整体移入系统层 —— 逐字复制 + `'../renderer'` / `'../use'` 8 处 `@ts-expect-error`(D-59 / D-67 / D-68)+ `client-base/src/index.ts` 再加 2 条桶 + 实现层 8 个 `render/ui` 文件 11 处 / `fallback/ui.ts` / `render/index.tsx:5` 改指 `@user/client-base` + 删除 18 个原件
+ Task 0 已闭环(用户已回复「可以执行」)且 Task 1 已交付(`client-base/src/{layout,shared}/`、`client-base/src/index.ts` 已含 `./layout` / `./shared`、`client-base/package.json` 已含 5 条依赖且 lockfile 已同步)
+ packages-user/client-base/src/components/**(13 新增), packages-user/client-base/src/elements/**(5 新增), packages-user/client-base/src/index.ts, packages-user/client-modules/src/render/ui/{main,save,settings,statistics,statusBar,title,toolbar,viewmap}.tsx, packages-user/client-modules/src/render/index.tsx, packages-user/client-modules/src/fallback/ui.ts, packages-user/client-modules/src/render/{components,elements}/**(18 删除)
+
+ - packages-user/client-modules/src/render/components/{index.ts,tip.tsx,textbox.tsx,textboxTyper.ts,misc.tsx,choices.tsx,scroll.tsx,input.tsx}(`'../renderer'` / `'../use'` 的当日行号与说明符原文;`@ts-expect-error` 标注的落点)
+ - packages-user/client-modules/src/render/elements/{index.ts,props.ts,cache.ts,misc.ts,types.ts}(`elements/index.ts:2` 的 `'../map'` 与 `:3` 的 `'../renderer'`;`createElements()` 的注册体——D-67 要求逐字随文件移动、逻辑零改动)
+ - packages-user/client-modules/src/render/ui/{main.tsx,save.tsx,settings.tsx,statistics.tsx,statusBar.tsx,title.tsx,toolbar.tsx,viewmap.tsx}(8 文件 11 处被改指 import 的当日行号;`toolbar.tsx` 的 `'../components/icons'` 与 `'../components/misc'` 两处深层导入)
+ - packages-user/client-modules/src/render/index.tsx:1-10(仅改 `:5` 的 `import { createElements } from './elements';`;`:45` / `:46` 留给 Task 3)
+ - packages-user/client-modules/src/fallback/ui.ts:1-4(`:2` 的深层导入 `'../render/components/tip'`)
+ - packages-user/client-base/src/index.ts(Task 1 之后的现状,本任务追加 `./components` 与 `./elements`)
+ - packages-user/client-modules/src/action/move.ts:1-6 与 packages-user/client-modules/src/render/ui/statistics.tsx:1-12(`// @ts-expect-error <原因>` 写在 import 块中间 import 行之上的既有先例)
+ - packages-user/client-modules/src/render/utils/saves.ts:1-6(既有 `'../components'` import;Task 3 才加 D-60 标注,本任务**不得**动它)
+ - dev.md(「模块原则」的引入位置;「类型规范」的 `// @ts-expect-error` 须说明原因;「其他要求」的 CRLF)
+
+
+ (1) **复制 `components`(13 文件,D-62)**:把 `packages-user/client-modules/src/render/components/` 下的 13 个文件(`choices.tsx` / `floorSelect.tsx` / `icons.tsx` / `index.ts` / `input.tsx` / `list.tsx` / `misc.tsx` / `page.tsx` / `scroll.tsx` / `textbox.tsx` / `textboxTyper.ts` / `thumbnail.tsx` / `tip.tsx`)**逐字**复制到 `packages-user/client-base/src/components/`(同名,不改内容、不改注释、不改 barrel 的 12 条 `export *`)。
+
+ (2) **复制 `elements`(5 文件,D-62 + D-67)**:把 `packages-user/client-modules/src/render/elements/` 下的 5 个文件(`cache.ts` / `index.ts` / `misc.ts` / `props.ts` / `types.ts`)**逐字**复制到 `packages-user/client-base/src/elements/`(同名)。**`elements/index.ts` 的 `createElements()` 与其 `mainRenderer.registerElement(...)` × 3 / `tagManager.registerTag(...)` × 3 注册体必须逐字随文件移动、逻辑零改动**(D-67:只移植,不接线)。其 `'../map'` 引用移动后仍解析到 `client-base/src/map`,**不得改动**。
+
+ (3) **架构耦合标注(D-59 / D-67 / D-68)**:在上述**新副本**中,对下列 8 处 import 行**保留说明符原文不变**(**不得**改成 `@user/client-modules` 或任何其它路径)并在其**上一行**插入 `// @ts-expect-error <简要原因>`;原因须写明「`render/renderer`(或 `render/use.ts`)按 D-54 留在实现层,本文件已移入系统层,按 D-59 / D-68 暂以标注记录,不反向引用、不解耦」这类可读说明(书写形态对齐 `packages-user/client-modules/src/action/move.ts:3-4` 的既有先例):
+ - `client-base/src/elements/index.ts`:`import { mainRenderer, tagManager } from '../renderer';`(D-67 的标签 / 元件注册耦合即由本标注覆盖——导入绑定降级后注册体本身不再新增标注)
+ - `client-base/src/components/textboxTyper.ts`:`import { using } from '../renderer';`
+ - `client-base/src/components/misc.tsx`:`import { using } from '../renderer';` 与 `import { transitioned } from '../use';`(同一文件 **2** 处)
+ - `client-base/src/components/choices.tsx`:`import { useKey } from '../use';`
+ - `client-base/src/components/input.tsx`:`import { transitionedColor, useKey } from '../use';`
+ - `client-base/src/components/scroll.tsx`:`import { transitioned } from '../use';`
+ - `client-base/src/components/tip.tsx`:`import { transitioned } from '../use';`
+ 被移文件内**既有**标注必须逐字保留(`components/textbox.tsx` 1 条、`components/textboxTyper.ts` 1 条、`elements/cache.ts` 4 条);**不得**在其它任何位置新增标注。
+
+ (4) **目标侧桶导出(D-63)**:在 `packages-user/client-base/src/index.ts` 追加两行 `export * from './components';` 与 `export * from './elements';`(保留 Task 1 已追加的 `./layout` / `./shared` 与原有 4 行原文)。
+
+ (5) **实现层消费者改指(D-63)**:把下列 import 语句的**说明符**替换为 `'@user/client-base'`(**只替换说明符文本**;不得合并 / 重排 / 拆分 import 语句,不得改动被导入符号列表,不得改动文件其它内容):
+ - `client-modules/src/render/ui/main.tsx`(`'../components'`)
+ - `client-modules/src/render/ui/save.tsx`(`'../components'`)
+ - `client-modules/src/render/ui/settings.tsx`(`'../components'` 两处)
+ - `client-modules/src/render/ui/statistics.tsx`(`'../components'`)
+ - `client-modules/src/render/ui/statusBar.tsx`(`'../components'`)
+ - `client-modules/src/render/ui/title.tsx`(`'../components'`)
+ - `client-modules/src/render/ui/viewmap.tsx`(`'../components'` 两处)
+ - `client-modules/src/render/ui/toolbar.tsx`(深层导入 `'../components/icons'` 与 `'../components/misc'` 两处)
+ - `client-modules/src/fallback/ui.ts`(深层导入 `'../render/components/tip'`)
+ - `client-modules/src/render/index.tsx`(仅 `:5` 的 `'./elements'`;`:45` / `:46` 留给 Task 3)
+ 以上共 **11** 处说明符(`render/ui` 8 文件)+ `fallback/ui.ts` 1 处 + `render/index.tsx` 1 处。
+
+ (6) **删除原件(MOVE 语义)**:删除 `packages-user/client-modules/src/render/components/` 下 13 个文件与 `packages-user/client-modules/src/render/elements/` 下 5 个文件(合计 **18**);删除后这两个目录内不得留有任何文件。`packages-user/client-modules/src/render/utils/{index.ts,saves.ts,use.ts}` 与 `render/` 顶层文件**不得**触碰(`saves.ts` 留待 Task 3 加 D-60 标注)。
+
+ (7) **CRLF 与收口**:确认本任务创建 / 修改的全部文件为 CRLF;本任务只允许改 (1)..(6) 列出的文件;**不得**改动 `client-base/package.json`(依赖已在 Task 1 补齐)、`pnpm-lock.yaml`、任何其它文件;不得新增计划文件;不得派发会写文件的子代理。
+
+ 本任务是同一结构迁移的第二切片(再移两个目录 + 再补两条桶 + 11 处说明符改指 + 18 个原件删除),全部改动均在 git 追踪范围内、无公共接口行为改动、无新命名,可用 `git revert` 或重跑本计划整体回退。
+
+
+ node -e "const fs=require('fs');const {execSync}=require('child_process');const head=execSync('git rev-parse HEAD',{encoding:'utf8'}).trim();const pairs=[['packages-user/client-base/src/components','components',13],['packages-user/client-base/src/elements','elements',5]];const bad=[];const ALLOW=/@ts-expect-error|String_X/;for(const [nd,sub,n] of pairs){if(!fs.existsSync(nd)){bad.push('MISSING DIR '+nd);continue}const files=fs.readdirSync(nd).filter(f=>fs.statSync(nd+'/'+f).isFile());if(files.length!==n)bad.push(nd+' FILE COUNT '+files.length+' expected '+n);for(const f of files){const op='packages-user/client-modules/src/render/'+sub+'/'+f;if(fs.existsSync(op))bad.push('ORIGINAL REMAINS '+op)}}for(const d of ['packages-user/client-modules/src/render/components','packages-user/client-modules/src/render/elements']){if(fs.existsSync(d)&&fs.readdirSync(d).length>0)bad.push('NOT EMPTY '+d)}if(bad.length>0){console.error('MOVE INCOMPLETE :: '+bad.join(' ; '));process.exit(1)}console.log('OK components 13 + elements 5 moved, 18 originals gone')"
+ non-zero exit, 或 stderr 含 "MOVE INCOMPLETE"(`components` / `elements` 目标文件数不是 13 / 5,或原件仍在,或原目录未清空 —— 移植未完成)
+ node -e "const fs=require('fs');const t=fs.readFileSync('packages-user/client-base/src/index.ts','utf8');const q=String.fromCharCode(39);const need=['./load','./material','./save','./types','./layout','./shared','./components','./elements'];const miss=need.filter(s=>t.indexOf('export * from '+q+s+q)<0);if(miss.length>0){console.error('BARREL MISSING '+miss.join(','));process.exit(1)}console.log('OK client-base barrel has 8 of 9 specifiers (map pending Task 3)')"
+ non-zero exit, 或 stderr 含 "BARREL MISSING"(`client-base/src/index.ts` 未追加 `./components` / `./elements`,或先前已追加的说明符 / 原有 4 行被改写)
+ node -e "const fs=require('fs');const dirs=['packages-user/client-base/src/components','packages-user/client-base/src/elements'];const exp={'components/textbox.tsx':1,'components/textboxTyper.ts':2,'components/misc.tsx':2,'components/choices.tsx':1,'components/input.tsx':1,'components/scroll.tsx':1,'components/tip.tsx':1,'elements/cache.ts':4,'elements/index.ts':1};const bad=[];let total=0;for(const d of dirs){for(const f of fs.readdirSync(d)){const p=d+'/'+f;const txt=fs.readFileSync(p,'utf8').replace(/\r\n/g,'\n');const lines=txt.split('\n');const idxs=lines.map((l,i)=>l.indexOf('@ts-expect-error')>=0?i:-1).filter(i=>i>=0);total+=idxs.length;const key=p.replace('packages-user/client-base/src/','');const want=exp[key]||0;if(idxs.length!==want)bad.push(key+' count '+idxs.length+' expected '+want);for(const i of idxs){if(!/@ts-expect-error\s+\S/.test(lines[i]))bad.push(key+':'+(i+1)+' BARE (no reason)')}}}if(total!==15){bad.push('client-base total annotations '+total+' expected 15')}if(bad.length>0){console.error('ANNOTATION GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK annotations: 15 total (7 pre-existing + 8 new), all with reasons')"
+ non-zero exit, 或 stderr 含 "ANNOTATION GATE FAILED" / "BARE"(`components` / `elements` 下的 `@ts-expect-error` 数量与「既有 7 + 新增 8」不符,或存在无原因的裸标注 —— D-59 / D-67 标注落点或 dev.md 的「须说明原因」被违反)
+ node -e "const fs=require('fs');const t=fs.readFileSync('packages-user/client-base/src/elements/index.ts','utf8').replace(/\r\n/g,'\n').split('\n');const i=t.findIndex(l=>l.indexOf(String.fromCharCode(39)+'../renderer'+String.fromCharCode(39))>=0);if(i<1){console.error('renderer import not found in elements/index.ts');process.exit(1)}if(t[i-1].indexOf('@ts-expect-error')<0){console.error('D-67 registration coupling NOT annotated');process.exit(1)}if(t.filter(l=>l.indexOf('registerElement(')>=0).length!==3||t.filter(l=>l.indexOf('registerTag(')>=0).length!==4||t.filter(l=>l.indexOf('createElements(')>=0).length!==1){console.error('createElements registration body changed (D-67)');process.exit(1)}console.log('OK D-67: registration body intact + coupling annotated')"
+ non-zero exit, 或 stderr 含 "renderer import not found" / "NOT annotated" / "registration body changed"(`elements/index.ts` 的 `'../renderer'` 未加 D-67 标注,或 `createElements()` 的 3 次 `registerElement` / 4 处 `registerTag(` 调用形态被改动 —— D-67 要求只移植不改逻辑)
+ node -e "const{execSync}=require('child_process');const specs=[[String.fromCharCode(39)+'../components'+String.fromCharCode(39),1],[String.fromCharCode(39)+'../components/icons'+String.fromCharCode(39),0],[String.fromCharCode(39)+'../components/misc'+String.fromCharCode(39),0],[String.fromCharCode(39)+'../render/components/tip'+String.fromCharCode(39),0],[String.fromCharCode(39)+'./elements'+String.fromCharCode(39),0]];const bad=[];for(const [pat,exp] of specs){let o='';try{o=execSync('git grep -n -F '+JSON.stringify(pat)+' -- packages-user/client-modules/src',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o=e.stdout||''}const n=o.replace(/\s+$/,'').length>0?o.split(/\r?\n/).length:0;if(n!==exp)bad.push(pat+' found '+n+' expected '+exp)}let o2='';try{o2=execSync('git grep -n -F '+JSON.stringify('@user/client-base')+' -- packages-user/client-modules/src',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o2=e.stdout||''}const n2=o2.replace(/\s+$/,'').length>0?o2.split(/\r?\n/).length:0;if(n2<15)bad.push('@user/client-base refs '+n2+' expected >=15');if(bad.length>0){console.error('CONSUMER REWIRE GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK consumer specifiers rewired ('+n2+' @user/client-base refs)')"
+ non-zero exit, 或 stderr 含 "CONSUMER REWIRE GATE FAILED"(`'../components'` 未收敛到仅剩 `render/utils/saves.ts` 的 1 处,或深层导入 `'../components/icons'` / `'../components/misc'` / `'../render/components/tip'` 未改指,或 `render/index.tsx` 的 `'./elements'` 未改指,或改指总数不足 —— D-63 未落地)
+ node -e "const fs=require('fs'),{execSync}=require('child_process');const head=execSync('git rev-parse HEAD',{encoding:'utf8'}).trim();const ALLOW=/@ts-expect-error|'\.\.\/(\.\.\/)?shared'/;const mult=a=>{const m=new Map();for(const l of a)m.set(l,(m.get(l)||0)+1);return m};const pairs=[['packages-user/client-base/src/components','components'],['packages-user/client-base/src/elements','elements']];const bad=[];for(const [nd,sub] of pairs){for(const f of fs.readdirSync(nd)){const op='packages-user/client-modules/src/render/'+sub+'/'+f;let orig='';try{orig=execSync('git show '+head+':'+op,{encoding:'utf8',maxBuffer:1e8})}catch(e){bad.push('NO GIT ORIGINAL '+op);continue}const a=orig.replace(/\r\n/g,'\n').split('\n'),b=fs.readFileSync(nd+'/'+f,'utf8').replace(/\r\n/g,'\n').split('\n');const ma=mult(a),mb=mult(b);const diff=[];for(const [l,c] of mb){const d=c-(ma.get(l)||0);for(let i=0;il.length>0&&!ALLOW.test(l));if(off.length>0)bad.push(nd+'/'+f+' :: '+off.slice(0,3).join(' | '))}}if(bad.length>0){console.error('UNAUTHORIZED MOVE DIFF :: '+bad.join(' ;; '));process.exit(1)}console.log('OK move diff limited to authorized lines (18 files)')"
+ non-zero exit, 或 stderr 含 "UNAUTHORIZED MOVE DIFF" / "NO GIT ORIGINAL"(被移文件相对原件出现授权面(shared 说明符 / `@ts-expect-error`)之外的任何行差异 —— 即注释被改写、逻辑被改动或发生了未授权重写,违反 D-62 / AGENTS.md 的注释不可改约束)
+ node -e "const fs=require('fs'),{execSync}=require('child_process');const head=execSync('git rev-parse HEAD',{encoding:'utf8'}).trim();const files=['packages-user/client-modules/src/render/ui/main.tsx','packages-user/client-modules/src/render/ui/save.tsx','packages-user/client-modules/src/render/ui/settings.tsx','packages-user/client-modules/src/render/ui/statistics.tsx','packages-user/client-modules/src/render/ui/statusBar.tsx','packages-user/client-modules/src/render/ui/title.tsx','packages-user/client-modules/src/render/ui/toolbar.tsx','packages-user/client-modules/src/render/ui/viewmap.tsx','packages-user/client-modules/src/render/index.tsx','packages-user/client-modules/src/fallback/ui.ts'];const ALLOW=/from '|@ts-expect-error/;const mult=a=>{const m=new Map();for(const l of a)m.set(l,(m.get(l)||0)+1);return m};const bad=[];for(const f of files){const a=execSync('git show '+head+':'+f,{encoding:'utf8',maxBuffer:1e8}).replace(/\r\n/g,'\n').split('\n');const b=fs.readFileSync(f,'utf8').replace(/\r\n/g,'\n').split('\n');const ma=mult(a),mb=mult(b);const diff=[];for(const [l,c] of mb){const d=c-(ma.get(l)||0);for(let i=0;il.length>0&&!ALLOW.test(l));if(off.length>0)bad.push(f+' :: '+off.slice(0,3).join(' | '));if(off.length===0&&diff.filter(l=>l.length>0).length===0)bad.push(f+' :: NO CHANGE')}if(bad.length>0){console.error('CONSUMER DIFF NOT IMPORT-ONLY :: '+bad.join(' ;; '));process.exit(1)}console.log('OK 10 consumer files changed on import lines only')"
+ non-zero exit, 或 stderr 含 "CONSUMER DIFF NOT IMPORT-ONLY" / "NO CHANGE"(实现层消费者除了说明符 / 标注行之外还有其它行变动 —— 越界修改消费者实现;或某个应改指的消费者实际未改)
+ node -e "const{execSync}=require('child_process');let hits=[];for(const pat of ['@user/client-modules','client-modules']){let o='',st=0;try{o=execSync('git grep -n -F '+JSON.stringify(pat)+' -- packages-user/client-base',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'');st=e.status}if(o.replace(/\s+$/,'').length>0)hits.push(pat+' => '+o.split(/\r?\n/).slice(0,3).join(' ; '));else if(st!==0&&st!==1)hits.push(pat+' grep status '+st)}if(hits.length>0){console.error('REVERSE DEPENDENCY DETECTED: '+hits.join(' || '));process.exit(1)}const o2=execSync('git status --porcelain -- packages-user/client-base/src/material packages-user/client-base/src/load packages packages/ src .planning/REQUIREMENTS.md',{encoding:'utf8'}).replace(/\s+$/,'');if(o2.length>0){console.error('FORBIDDEN PATH TOUCHED: '+o2);process.exit(1)}console.log('OK no reverse dependency; forbidden paths clean')"
+ non-zero exit, 或 stderr 含 "REVERSE DEPENDENCY DETECTED" / "FORBIDDEN PATH TOUCHED"(`client-base` 出现对 `client-modules` 的引用,或 `material/` / `load/` / `packages/` / `src/` / `REQUIREMENTS.md` 被改动)
+ node -e "const fs=require('fs');const files=['packages-user/client-base/src/index.ts'];for(const d of ['packages-user/client-base/src/components','packages-user/client-base/src/elements'])for(const f of fs.readdirSync(d))files.push(d+'/'+f);files.push('packages-user/client-modules/src/render/ui/main.tsx','packages-user/client-modules/src/render/ui/save.tsx','packages-user/client-modules/src/render/ui/settings.tsx','packages-user/client-modules/src/render/ui/statistics.tsx','packages-user/client-modules/src/render/ui/statusBar.tsx','packages-user/client-modules/src/render/ui/title.tsx','packages-user/client-modules/src/render/ui/toolbar.tsx','packages-user/client-modules/src/render/ui/viewmap.tsx','packages-user/client-modules/src/render/index.tsx','packages-user/client-modules/src/fallback/ui.ts');const bad=[];for(const f of files){const s=fs.readFileSync(f,'utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf)bad.push(f+' ('+(lf-crlf)+' stray LF)')}if(bad.length>0){console.error('NON-CRLF '+bad.join(' ; '));process.exit(1)}console.log('OK CRLF '+files.length+' files')"
+ non-zero exit, 或 stderr 含 "NON-CRLF"(本任务创建 / 修改的文件出现非 CRLF 换行)
+
+
+ - `packages-user/client-base/src/components/` 含 **13** 个文件、`packages-user/client-base/src/elements/` 含 **5** 个文件,均为同名逐字副本;`packages-user/client-modules/src/render/components/` 与 `render/elements/` 内**零文件**
+ - 被移文件内**既有 7 条** `@ts-expect-error`(`components/textbox.tsx` 1 / `components/textboxTyper.ts` 1 / `elements/cache.ts` 4,`map/extension/hero.ts` 1 由 Task 3 覆盖)逐字保留;`components` + `elements` 两目录内标注**总数恰为 15**(7 既有 + 8 新增)且每行均带非空原因
+ - 新增 8 处标注落在 D-59 / D-67 的授权落点:`elements/index.ts`(`'../renderer'`)、`components/{textboxTyper.ts,misc.tsx}`(`'../renderer'`)、`components/{choices.tsx,input.tsx,misc.tsx,scroll.tsx,tip.tsx}`(`'../use'`);相应 import **说明符原文未变**(**无** `@user/client-modules` 反向引用)
+ - `elements/index.ts` 的 `createElements()` 与 3 次 `registerElement` + 4 处 `registerTag(` 调用形态逐字未变(D-67 只移植不接线)
+ - `packages-user/client-base/src/index.ts` 已含 `./components` 与 `./elements`(此时共 8 条说明符,`./map` 待 Task 3)
+ - `'../components'` 在 `packages-user/client-modules/src` 内仅剩 `render/utils/saves.ts` 的 **1** 处;`'../components/icons'` / `'../components/misc'` / `'../render/components/tip'` / `'./elements'` 均为 **0** 处;`@user/client-base` 引用数 ≥ **15**
+ - 被移 18 文件相对原件的差异**仅限**授权面(`@ts-expect-error` / shared 说明符),注释与逻辑零改写;10 个实现层消费者文件的差异**仅在 import / 标注行**
+ - `client-base` 内 `@user/client-modules` / `client-modules` 零命中;`material/` / `load/` / `packages/` / `src/` / `REQUIREMENTS.md` 零改动;全部文件 CRLF;未触碰本任务范围外文件
+
+ `components`(13)+ `elements`(5)两目录整体移入系统层并删除 18 个原件;`'../renderer'` / `'../use'` 共 8 处按 D-59 / D-67 / D-68 加带原因标注且不反向引用;`client-base/src/index.ts` 再加 2 条桶;`render/ui` 8 文件 11 处 + `fallback/ui.ts` + `render/index.tsx:5` 改指 `@user/client-base`;允许差异 / 标注 / 消费者改指 / 反向引用 / 范围 / CRLF 六道门禁全绿;SUMMARY 记录 D-67 的注册体零改动与 D-63 的消费者改指面;仅剩 `map` + 深层消费者
+
+
+
+ Task 3:`map`(20)文件夹整体移入系统层 —— 逐字复制 + shared 说明符改指 4 处(D-58)+ `client-base/src/index.ts` 补最后 1 条桶 + `client.ts` / `types.ts` / `render/index.tsx:45,46` 改指 + `render/utils/saves.ts:2` 的 `@ts-expect-error`(D-60)+ 删除 20 个原件
+ Task 0 已闭环(用户已回复「可以执行」)且 Task 1 / Task 2 已交付(`client-base/src/{layout,shared,components,elements}/` 均在建、`client-base/src/index.ts` 已含 8 条说明符、`client-base/package.json` 已含 5 条依赖且 lockfile 已同步)
+ packages-user/client-base/src/map/**(20 新增,含 extension/ 与 shader/), packages-user/client-base/src/index.ts, packages-user/client-modules/src/client.ts, packages-user/client-modules/src/types.ts, packages-user/client-modules/src/render/index.tsx, packages-user/client-modules/src/render/utils/saves.ts, packages-user/client-modules/src/render/map/**(20 删除)
+
+ - packages-user/client-modules/src/render/map/{element.ts,renderer.ts,vertex.ts}(`'../../shared'` 的当日行号 —— `element.ts:5`、`renderer.ts:44-53` 的 import 块尾、`vertex.ts:19-25` 的 import 块尾;改指为 `'../shared'`)
+ - packages-user/client-modules/src/render/map/extension/door.ts:9(`'../../../shared'`;改指为 `'../../shared'`)
+ - packages-user/client-modules/src/render/map/{renderer.ts:43-46 的 4 条 `?raw` import, index.ts 的 `'./extension'` + 9 条 `export *`, extension/index.ts 的 3 条 `export *`}(**相对路径不变,一律不动**:D-64 明示 `?raw` 不需要处理)
+ - packages-user/client-modules/src/render/map/{extension/hero.ts:16, moving.ts:3, renderer.ts:13, types.ts:12, vertex.ts:28}(`@user/client-base` 自包引用 5 处 —— 说明符**保持不变**、**不加标注**)
+ - packages-user/client-modules/src/render/map/extension/hero.ts:122(既有标注,须逐字保留)
+ - packages-user/client-modules/src/client.ts:15-26 与 packages-user/client-modules/src/types.ts:1-3(`'./render/map'` 的 2 处改指)
+ - packages-user/client-modules/src/render/index.tsx:44-52(`:45` `export * from './components';`、`:46` `export * from './elements';` 改指为 `export * from '@user/client-base';`;`:51` 的 `'./renderer'` 不动)
+ - packages-user/client-modules/src/render/utils/saves.ts:1-6(`:2` 的 `'../components'`;D-60 标注点;`:5` 的 `'../../shared'` 属实现层自己的 shared,**不得**改)
+ - packages-user/client-base/src/index.ts(Task 2 之后的现状,本任务补 `./map`)
+ - packages-user/client-modules/src/action/move.ts:1-6(`// @ts-expect-error <原因>` 的书写先例)
+ - dev.md(「模块原则」的引入位置 / 无类型导入;「类型规范」的 `// @ts-expect-error` 须说明原因;「其他要求」的 CRLF)
+
+
+ (1) **复制 `map`(20 文件,D-62 + D-64)**:把 `packages-user/client-modules/src/render/map/` 下的 **20** 个文件**逐字**复制到 `packages-user/client-base/src/map/`,保持同名相对结构:顶层 10(`block.ts` / `constant.ts` / `element.ts` / `index.ts` / `moving.ts` / `renderer.ts` / `status.ts` / `types.ts` / `vertex.ts` / `viewport.ts`)+ `extension/` 6(`door.ts` / `hero.ts` / `index.ts` / `manager.ts` / `text.ts` / `types.ts`)+ `shader/` 4(`back.frag` / `back.vert` / `map.frag` / `map.vert`)。**`renderer.ts` 的 4 条 `?raw` import(`'./shader/map.vert?raw'` / `'./shader/map.frag?raw'` / `'./shader/back.vert?raw'` / `'./shader/back.frag?raw'`)、`map/index.ts` 的 `'./extension'` + 9 条 `export *`、`extension/index.ts` 的 3 条 `export *`、以及全部集合内部相对引用(`'../types'` 等)一律逐字不动**(D-64 / R5)。
+
+ (2) **shared 说明符改指(D-58 / R1)**:在**新副本**中只改这 4 处说明符,其余一律不动:
+ - `client-base/src/map/element.ts`:`'../../shared'` → `'../shared'`
+ - `client-base/src/map/renderer.ts`:`'../../shared'` → `'../shared'`(import 块尾行)
+ - `client-base/src/map/vertex.ts`:`'../../shared'` → `'../shared'`(import 块尾行)
+ - `client-base/src/map/extension/door.ts`:`'../../../shared'` → `'../../shared'`
+ 改指后这 4 处的目标均为 `packages-user/client-base/src/shared/`(Task 1 已建),**不得**改指到 `@user/client-modules` 或 `@user/client-base`;`packages-user/client-modules/src/shared.ts` **不得**改动。
+
+ (3) **自包引用保持原文(R4)**:5 处 `@user/client-base` import(`map/extension/hero.ts` / `map/moving.ts` / `map/renderer.ts` / `map/types.ts` / `map/vertex.ts`)说明符**逐字保持不变**,**不得**为其新增 `// @ts-expect-error`,**不得**改写成相对路径或包内路径。既有 `map/extension/hero.ts` 的 1 条带原因标注逐字保留。
+
+ (4) **目标侧桶导出收尾(D-63)**:在 `packages-user/client-base/src/index.ts` 追加最后一行 `export * from './map';`(此时 5 条新说明符 + 原有 4 行齐备)。
+
+ (5) **实现层消费者改指与标注(D-63 + D-60)**:
+ - `packages-user/client-modules/src/client.ts`:`'./render/map'` → `'@user/client-base'`(**只替换说明符**;该文件 `:19` 已有 `@user/client-base` import,允许两行同源 import,**不得**合并 / 重排)
+ - `packages-user/client-modules/src/types.ts`:`'./render/map'` → `'@user/client-base'`(只替换说明符)
+ - `packages-user/client-modules/src/render/index.tsx:45`:`export * from './components';` → `export * from '@user/client-base';`;`:46`:`export * from './elements';` → `export * from '@user/client-base';`(**逐行 1:1 替换、保留两行**;改指后两行同源,该形态按 D-68 **保留**,不得在本步合并 / 去重 —— 是否合并由用户后续裁定)
+ - `packages-user/client-modules/src/render/utils/saves.ts:2`:`import { getConfirm, waitbox } from '../components';` 保留原文,在其**上一行**插入 `// @ts-expect-error <简要原因>`(原因须写明「`components` 已按 D-58..D-68 移植到 `@user/client-base`,本文件按 D-60 暂以标注记录断链」;该文件 `:5` 的 `'../../shared'` 属实现层自己的 shared,**不得**改动)
+ - `render/index.tsx:51`(`export * from './renderer';`)与 `render/index.tsx:49`(`export * from '../shared';`)**不得**触碰。
+
+ (6) **删除原件(MOVE 语义)**:删除 `packages-user/client-modules/src/render/map/` 下 **20** 个文件(含 `extension/` 6 与 `shader/` 4);删除后该目录内不得留有任何文件。至此 `packages-user/client-modules/src/render/` 下应仅剩顶层文件(`action.ts` / `index.tsx` / `renderer.ts` / `scene.ts` / `use.ts`)、`ui/` / `fx/` / `weather/` / `utils/`。
+
+ (7) **CRLF 与收口**:确认本任务创建 / 修改的全部文件为 CRLF;本任务只允许改 (1)..(6) 列出的文件;**不得**改动 `client-base/package.json` 与 `pnpm-lock.yaml`(依赖已在 Task 1 补齐;若发现 `map` 引入了尚缺的依赖,**停止并告知用户**,不得擅自改 `package.json`);不得新增计划文件;不得派发会写文件的子代理。
+
+ 本任务是同一结构迁移的第三切片(移入最大目录 + 补最后一条桶 + 3 个深层消费者改指 + 1 处 D-60 标注 + 20 个原件删除),全部改动在 git 追踪范围内、无公共接口行为与新命名,可用 `git revert` 或重跑本计划整体回退。
+
+
+ node -e "const fs=require('fs');const sub=['block.ts','constant.ts','element.ts','index.ts','moving.ts','renderer.ts','status.ts','types.ts','vertex.ts','viewport.ts','extension/door.ts','extension/hero.ts','extension/index.ts','extension/manager.ts','extension/text.ts','extension/types.ts','shader/back.frag','shader/back.vert','shader/map.frag','shader/map.vert'];const bad=[];for(const f of sub){if(!fs.existsSync('packages-user/client-base/src/map/'+f))bad.push('MISSING '+f);if(fs.existsSync('packages-user/client-modules/src/render/map/'+f))bad.push('ORIGINAL REMAINS '+f)}const d='packages-user/client-modules/src/render/map';if(fs.existsSync(d)&&fs.readdirSync(d).length>0)bad.push('NOT EMPTY '+d);if(bad.length>0){console.error('MAP MOVE INCOMPLETE :: '+bad.join(' ; '));process.exit(1)}console.log('OK map 20 moved, 20 originals gone')"
+ non-zero exit, 或 stderr 含 "MAP MOVE INCOMPLETE"(`map` 20 个目标文件不全,或原件仍在 / 原目录未清空)
+ node -e "const fs=require('fs');const q=String.fromCharCode(39);const exp={['packages-user/client-base/src/map/element.ts']:1,['packages-user/client-base/src/map/renderer.ts']:1,['packages-user/client-base/src/map/vertex.ts']:1};const bad=[];for(const [f,n] of Object.entries(exp)){const t=fs.readFileSync(f,'utf8').replace(/\r\n/g,'\n');const a=(t.match(new RegExp('from '+q+'../shared'+q,'g'))||[]).length;const b=(t.match(new RegExp('from '+q+'../../shared'+q,'g'))||[]).length;if(a!==n||b!==0)bad.push(f+' ../shared='+a+' ../../shared='+b)}const dt=fs.readFileSync('packages-user/client-base/src/map/extension/door.ts','utf8').replace(/\r\n/g,'\n');const da=(dt.match(new RegExp('from '+q+'../../shared'+q,'g'))||[]).length;const db=(dt.match(new RegExp('from '+q+'../../../shared'+q,'g'))||[]).length;if(da!==1||db!==0)bad.push('door.ts ../../shared='+da+' ../../../shared='+db);const raw=fs.readFileSync('packages-user/client-base/src/map/renderer.ts','utf8');for(const r of ['shader/map.vert?raw','shader/map.frag?raw','shader/back.vert?raw','shader/back.frag?raw'])if(raw.indexOf(r)<0)bad.push('?raw lost '+r);const sh=fs.readFileSync('packages-user/client-modules/src/shared.ts','utf8');if(sh.indexOf('DOOR_ANIMATE_INTERVAL')<0)bad.push('client-modules/src/shared.ts modified');if(bad.length>0){console.error('SHARED REWIRE GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK shared specifiers rewritten (3x ../shared, 1x ../../shared) + ?raw intact')"
+ non-zero exit, 或 stderr 含 "SHARED REWIRE GATE FAILED" / "?raw lost"(shared 说明符未按 D-58 / R1 改指,或 `?raw` 相对路径被改动(D-64),或 `client-modules/src/shared.ts` 被改动)
+ node -e "const fs=require('fs');const t=fs.readFileSync('packages-user/client-base/src/index.ts','utf8');const q=String.fromCharCode(39);const need=['./load','./material','./save','./types','./components','./elements','./map','./layout','./shared'];const miss=need.filter(s=>t.indexOf('export * from '+q+s+q)<0);if(miss.length>0){console.error('BARREL MISSING '+miss.join(','));process.exit(1)}console.log('OK client-base barrel complete: 5 new + 4 original')"
+ non-zero exit, 或 stderr 含 "BARREL MISSING"(`client-base/src/index.ts` 的 5 条桶导出不全,或原有 4 行被改写 —— D-63 未完全落地)
+ node -e "const fs=require('fs');const t=fs.readFileSync('packages-user/client-modules/src/render/utils/saves.ts','utf8').replace(/\r\n/g,'\n').split('\n');const i=t.findIndex(l=>l.indexOf(String.fromCharCode(39)+'../components'+String.fromCharCode(39))>=0);if(i<1){console.error('components import not found in saves.ts');process.exit(1)}if(t[i-1].indexOf('@ts-expect-error')<0){console.error('D-60 annotation MISSING');process.exit(1)}if(!/@ts-expect-error\s+\S/.test(t[i-1])){console.error('BARE @ts-expect-error');process.exit(1)}const j=t.findIndex(l=>l.indexOf(String.fromCharCode(39)+'../../shared'+String.fromCharCode(39))>=0);if(j<0){console.error('saves.ts own shared import modified');process.exit(1)}console.log('OK D-60 annotation on render/utils/saves.ts; own shared import intact')"
+ non-zero exit, 或 stderr 含 "components import not found" / "D-60 annotation MISSING" / "BARE" / "own shared import modified"(`render/utils/saves.ts:2` 未按 D-60 加带原因标注,或误改了该文件自己的 `'../../shared'` 引用)
+ node -e "const{execSync}=require('child_process');const specs=[[String.fromCharCode(39)+'./render/map'+String.fromCharCode(39),0],[String.fromCharCode(39)+'./components'+String.fromCharCode(39),0],[String.fromCharCode(39)+'../components'+String.fromCharCode(39),1],[String.fromCharCode(39)+'./layout'+String.fromCharCode(39),1]];const bad=[];for(const [pat,exp] of specs){let o='';try{o=execSync('git grep -n -F '+JSON.stringify(pat)+' -- packages-user/client-modules/src',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o=e.stdout||''}const n=o.replace(/\s+$/,'').length>0?o.split(/\r?\n/).length:0;if(n!==exp)bad.push(pat+' found '+n+' expected '+exp)}const rt=fs.readFileSync('packages-user/client-modules/src/render/index.tsx','utf8');const ex=(rt.match(/export \* from '@user\/client-base';/g)||[]).length;if(ex!==2)bad.push('render/index.tsx @user/client-base star exports '+ex+' expected 2');if(rt.indexOf('from '+String.fromCharCode(39)+'./renderer'+String.fromCharCode(39))<0)bad.push('render/index.tsx renderer export touched');let o2='';try{o2=execSync('git grep -n -F '+JSON.stringify('@user/client-base')+' -- packages-user/client-modules/src',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o2=e.stdout||''}const n2=o2.replace(/\s+$/,'').length>0?o2.split(/\r?\n/).length:0;if(n2<19)bad.push('@user/client-base refs '+n2+' expected >=19');if(bad.length>0){console.error('DEEP CONSUMER REWIRE GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK deep consumers rewired ('+n2+' @user/client-base refs)')"
+ non-zero exit, 或 stderr 含 "DEEP CONSUMER REWIRE GATE FAILED"(`'./render/map'` 未改指为 0,或 `render/index.tsx:45,46` 未成为两条 `@user/client-base` 星导出 / 误动了 `'./renderer'`,或 `render/utils/saves.ts` 的 `'../components'` 被误改(应为 1)、`render/utils/index.ts` 的 `'./layout'` 被误改(应为 1)—— D-63 / D-60 未落地)
+ node -e "const fs=require('fs'),{execSync}=require('child_process');const head=execSync('git rev-parse HEAD',{encoding:'utf8'}).trim();const ALLOW=/@ts-expect-error|'\.\.\/(\.\.\/)?shared'/;const mult=a=>{const m=new Map();for(const l of a)m.set(l,(m.get(l)||0)+1);return m};const files=['block.ts','constant.ts','element.ts','index.ts','moving.ts','renderer.ts','status.ts','types.ts','vertex.ts','viewport.ts','extension/door.ts','extension/hero.ts','extension/index.ts','extension/manager.ts','extension/text.ts','extension/types.ts','shader/back.frag','shader/back.vert','shader/map.frag','shader/map.vert'];const bad=[];for(const f of files){const nf='packages-user/client-base/src/map/'+f,of='packages-user/client-modules/src/render/map/'+f;const a=execSync('git show '+head+':'+of,{encoding:'utf8',maxBuffer:1e8}).replace(/\r\n/g,'\n').split('\n');const b=fs.readFileSync(nf,'utf8').replace(/\r\n/g,'\n').split('\n');const ma=mult(a),mb=mult(b);const diff=[];for(const [l,c] of mb){const d=c-(ma.get(l)||0);for(let i=0;il.length>0&&!ALLOW.test(l));if(off.length>0)bad.push(nf+' :: '+off.slice(0,3).join(' | '));}if(bad.length>0){console.error('UNAUTHORIZED MAP DIFF :: '+bad.join(' ;; '));process.exit(1)}console.log('OK map move diff limited to authorized lines (20 files)')"
+ non-zero exit, 或 stderr 含 "UNAUTHORIZED MAP DIFF"(被移 `map` 文件相对原件出现授权面之外的任何行差异 —— 含自包引用被改写、`?raw` 被改动、注释被改写等,违反 D-62 / R4 / R5)
+ node -e "const fs=require('fs'),{execSync}=require('child_process');const head=execSync('git rev-parse HEAD',{encoding:'utf8'}).trim();const files=['packages-user/client-modules/src/client.ts','packages-user/client-modules/src/types.ts','packages-user/client-modules/src/render/index.tsx'];const ALLOW=/from '|export \* from|@ts-expect-error/;const mult=a=>{const m=new Map();for(const l of a)m.set(l,(m.get(l)||0)+1);return m};const bad=[];for(const f of files){const a=execSync('git show '+head+':'+f,{encoding:'utf8',maxBuffer:1e8}).replace(/\r\n/g,'\n').split('\n');const b=fs.readFileSync(f,'utf8').replace(/\r\n/g,'\n').split('\n');const ma=mult(a),mb=mult(b);const diff=[];for(const [l,c] of mb){const d=c-(ma.get(l)||0);for(let i=0;il.length>0&&!ALLOW.test(l));if(off.length>0)bad.push(f+' :: '+off.slice(0,3).join(' | '))}if(bad.length>0){console.error('DEEP CONSUMER DIFF NOT IMPORT-ONLY :: '+bad.join(' ;; '));process.exit(1)}console.log('OK client.ts / types.ts / render/index.tsx changed on import-export lines only')"
+ non-zero exit, 或 stderr 含 "DEEP CONSUMER DIFF NOT IMPORT-ONLY"(`client.ts` / `types.ts` / `render/index.tsx` 除 import / export 说明符行之外还有其它行变动 —— 越界修改实现层实现)
+ node -e "const fs=require('fs'),{execSync}=require('child_process');const all=[];const walk=d=>{for(const f of fs.readdirSync(d)){const p=d+'/'+f;fs.statSync(p).isDirectory()?walk(p):all.push(p)}};walk('packages-user/client-base/src');const bad=[];let n=0;for(const p of all){const t=fs.readFileSync(p,'utf8').replace(/\r\n/g,'\n').split('\n');t.forEach((l,i)=>{if(l.indexOf('@ts-expect-error')>=0){n++;if(!/@ts-expect-error\s+\S/.test(l))bad.push(p+':'+(i+1)+' BARE')}})}if(n!==15)bad.push('client-base annotation total '+n+' expected 15');const ut=fs.readFileSync('packages-user/client-modules/src/render/utils/index.ts','utf8').replace(/\r\n/g,'\n').split('\n');const ua=ut.filter(l=>l.indexOf('@ts-expect-error')>=0);if(ua.length!==1||!/@ts-expect-error\s+\S/.test(ua[0]))bad.push('render/utils/index.ts annotation count '+ua.length);const sa=fs.readFileSync('packages-user/client-modules/src/render/utils/saves.ts','utf8').replace(/\r\n/g,'\n').split('\n').filter(l=>l.indexOf('@ts-expect-error')>=0);if(sa.length!==1||!/@ts-expect-error\s+\S/.test(sa[0]))bad.push('render/utils/saves.ts annotation count '+sa.length);let hits=[];for(const pat of ['@user/client-modules','client-modules']){let o='',st=0;try{o=execSync('git grep -n -F '+JSON.stringify(pat)+' -- packages-user/client-base',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'');st=e.status}if(o.replace(/\s+$/,'').length>0)hits.push(pat+' => '+o.split(/\r?\n/).slice(0,3).join(' ; '));else if(st!==0&&st!==1)hits.push(pat+' grep status '+st)}if(hits.length>0)bad.push('REVERSE DEP '+hits.join(' || '));const o2=execSync('git status --porcelain -- packages-user/client-base/src/material packages-user/client-base/src/load packages packages/ src .planning/REQUIREMENTS.md',{encoding:'utf8'}).replace(/\s+$/,'');if(o2.length>0)bad.push('FORBIDDEN PATH TOUCHED '+o2);if(bad.length>0){console.error('ANNOTATION+SCOPE GATE FAILED :: '+bad.join(' ;; '));process.exit(1)}console.log('OK annotations 15+1+1 all with reasons; no reverse dep; forbidden paths clean')"
+ non-zero exit, 或 stderr 含 "ANNOTATION+SCOPE GATE FAILED" / "BARE"(`client-base/src` 内 `@ts-expect-error` 总数不是 15,或 `render/utils/{index,saves}.ts` 各不足 1 条带原因标注,或出现裸标注,或出现反向依赖,或禁用路径被改动)
+ node -e "const fs=require('fs');const files=[];const walk=d=>{for(const f of fs.readdirSync(d)){const p=d+'/'+f;fs.statSync(p).isDirectory()?walk(p):files.push(p)}};walk('packages-user/client-base/src');files.push('packages-user/client-modules/src/client.ts','packages-user/client-modules/src/types.ts','packages-user/client-modules/src/render/index.tsx','packages-user/client-modules/src/render/utils/index.ts','packages-user/client-modules/src/render/utils/saves.ts');const bad=[];for(const f of files){const s=fs.readFileSync(f,'utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf)bad.push(f+' ('+(lf-crlf)+' stray LF)')}if(bad.length>0){console.error('NON-CRLF '+bad.join(' ; '));process.exit(1)}console.log('OK CRLF '+files.length+' files')"
+ non-zero exit, 或 stderr 含 "NON-CRLF"(`client-base/src` 下任一文件或本任务改动的实现层文件出现非 CRLF 换行)
+
+
+ - `packages-user/client-base/src/map/` 含 **20** 个文件(顶层 10 + `extension/` 6 + `shader/` 4,含 4 个二进制等价文本 shader);`packages-user/client-modules/src/render/map/` 内**零文件**
+ - shared 说明符改指恰好 **4** 处:`map/{element,renderer,vertex}.ts` 各 1 处 `'../shared'`(且各有 0 处 `'../../shared'`)、`map/extension/door.ts` 1 处 `'../../shared'`(且 0 处 `'../../../shared'`);`packages-user/client-modules/src/shared.ts` 未改动
+ - `map/renderer.ts` 的 4 条 `?raw` import 与 `map/index.ts` / `map/extension/index.ts` 的桶导出逐字未变(D-64 / R5);`map/*` 内 5 处 `@user/client-base` 自包引用说明符未变且未新增标注(R4);既有 `map/extension/hero.ts` 标注逐字保留
+ - `packages-user/client-base/src/index.ts` 含全部 **5** 条新桶导出(`./components` / `./elements` / `./map` / `./layout` / `./shared`)且原有 4 行原文保留
+ - `'./render/map'` 在 `packages-user/client-modules/src` 内为 **0**;`render/index.tsx` 出现 **2** 条 `export * from '@user/client-base';` 且 `'./renderer'` 未被动;`'./components'` 为 0、`'../components'` 为 1(`render/utils/saves.ts`)、`'./layout'` 为 1(`render/utils/index.ts`);`@user/client-base` 引用数 ≥ **19**
+ - `render/utils/saves.ts:2` 上一行新增带原因的 `// @ts-expect-error`(D-60),该文件自己的 `'../../shared'` 引用未动
+ - `client-base/src/**` 内 `@ts-expect-error` 总数恰为 **15**(7 既有 + 8 新增),`render/utils/{index,saves}.ts` 各 1 条;全部带非空原因,无裸标注
+ - 被移 20 文件的差异仅限授权面(`@ts-expect-error` / shared 说明符);`client.ts` / `types.ts` / `render/index.tsx` 的差异仅在 import / export 说明符行
+ - `client-base` 内 `@user/client-modules` / `client-modules` 零命中;`material/` / `load/` / `packages/` / `src/` / `REQUIREMENTS.md` 零改动;全部文件 CRLF
+
+ `map` 20 文件整体移入系统层并删除 20 个原件;shared 说明符按 D-58 改指 4 处(`'../shared'` × 3 / `'../../shared'` × 1);`client-base/src/index.ts` 的 5 条桶导出齐备;`client.ts` / `types.ts` / `render/index.tsx:45,46` 改指 `@user/client-base`;`render/utils/saves.ts:2` 按 D-60 加带原因标注;自包引用与 `?raw` 与集合内部相对路径逐字未动;允许差异 / 深层改指 / 标注 / 反向引用 / 范围 / CRLF 六道门禁全绿;39 文件迁移全部完成,仅剩收口
+
+
+
+ Task 4:收口 —— 39 文件全量允许差异比对 + 12 道静态门禁终跑(含 porcelain 范围门禁与计划文件范围门禁)+ 人工复核 + `04-11-SUMMARY.md` 落盘 + 按白名单暂存并提交
+ Task 0 已闭环(用户已回复「可以执行」)且 Task 1 / 2 / 3 均已交付(39 个文件全部移入 `client-base/src/`、原件全部删除、桶 / 依赖 / 消费者改指 / 标注全部落地)
+ packages-user/client-base/**(41 新增 + index.ts / package.json), packages-user/client-modules/src/{client.ts,types.ts}, packages-user/client-modules/src/render/{index.tsx,ui/**,utils/{index,saves}.ts}, packages-user/client-modules/src/fallback/ui.ts, packages-user/client-modules/src/render/{components,elements,map}/**(39 删除), packages-user/client-modules/src/render/utils/layout.ts(删除), pnpm-lock.yaml, .planning/phases/04-render-adaptation/04-11-SUMMARY.md
+
+ - .planning/phases/04-render-adaptation/04-11-PLAN.md(全部门禁与禁令;本任务是终跑与收口)
+ - .planning/phases/04-render-adaptation/04-10-PLAN.md:405-479(上一增量 Task 3 收口任务的写法:门禁逐道跑、人工复核、SUMMARY 落盘、白名单提交)
+ - .planning/phases/04-render-adaptation/04-09-SUMMARY.md:1-45,93-131(既有 SUMMARY 的元信息块 / actuals / 门禁摘要 / 人工复核 / 无新命名声明 / 运行时不可得声明 / 范围外已知余留的写法)
+ - .planning/phases/04-render-adaptation/04-STRUCTURE-MIGRATION-IMPACT.md:124-236(台账 A / B / C / D / E / F 六类,本任务的人工复核抽样依据)
+ - packages-user/client-base/src/{index.ts,package.json}(终态核对:9 条桶说明符 / 5 + 5 条依赖)
+ - dev.md(「其他要求」的 CRLF;提交前不得触碰用户并发改动)
+
+
+ (1) **12 道静态门禁终跑(按实测结果如实记录,任一失败即回修后重跑,不得改门禁口径、不得伪造通过)**:
+ - ① **目标落位门禁**:`client-base/src/{components,elements,map,layout,shared}/` 下 41 个文件存在(13 + 5 + 20 + `layout/{layout,index}.ts` + `shared/{shared,index}.ts`);`client-base/src/render/` 不存在。
+ - ② **移动已发生门禁**:`files_deleted` 的 39 条逐条不存在;`client-modules/src/render/{components,elements,map}` 三目录内零文件;`client-modules/src/render/utils/layout.ts` 不存在;`client-modules/src/shared.ts` **存在**(D-58);`client-modules/src/render/utils/{index.ts,saves.ts,use.ts}` 存在。
+ - ③ **桶导出门禁**:`client-base/src/index.ts` 含 9 条 `export * from`(原 4 + 新 5:`./components` / `./elements` / `./map` / `./layout` / `./shared`)。
+ - ④ **依赖与 lockfile 门禁**:`client-base/package.json` 的 5 条新依赖齐备、原 5 条保留;`pnpm-lock.yaml` 的 `packages-user/client-base` importer 块同步 5 条。
+ - ⑤ **消费者改指门禁**:`'./render/map'` 0、`'./components'` 0、`'./elements'` 0、`'./layout'` 1(`render/utils/index.ts`)、`'../components'` 1(`render/utils/saves.ts`)、`'../components/icons'` 0、`'../components/misc'` 0、`'../render/components/tip'` 0;`render/index.tsx` 出现 2 条 `export * from '@user/client-base';` 且 `'./renderer'` / `'../shared'` 未动;`@user/client-base` 引用数 ≥ 19。
+ - ⑥ **反向引用零命中门禁**:`git grep -n -F "@user/client-modules" -- packages-user/client-base` 与 `git grep -n -F "client-modules" -- packages-user/client-base` 均零命中(退出码 1)。
+ - ⑦ **`@ts-expect-error` 落点门禁**:`client-base/src/**` 内总数恰 **15**(7 既有 + 8 新增)、`client-modules/src/render/utils/{index,saves}.ts` 各 **1** 条,且**全部**形如 `// @ts-expect-error <非空原因>`(无裸标注);全仓新增标注仅这 10 处。
+ - ⑧ **允许差异门禁(39 文件全量)**:对全部 39 个被移文件用「多重集差」把新副本与 `git show :<原路径>` 逐一比对,**除授权面**(`@ts-expect-error` 插入;`'../../shared'` → `'../shared'`、`'../../../shared'` → `'../../shared'`)以外的任何行差异一律失败;`client-base/src/layout/layout.ts` 须与原件逐字相同;`client-base/src/shared/shared.ts` 的每一非空行须逐字出现在 `client-modules/src/shared.ts` 中。
+ - ⑨ **实现层差异门禁**:14 个消费者文件的差异**只**允许落在 import / export 说明符行与新增标注行;`client-modules/src/render/{action,fx,weather,scene,renderer.ts,use.ts}` 与非消费者的其它文件**零改动**。
+ - ⑩ **范围与用户改动门禁**:`git status --porcelain -- packages-user/client-base/src/material packages-user/client-base/src/load packages packages/ src .planning/REQUIREMENTS.md .planning/phases/04-render-adaptation/04-01-PLAN.md`(及 04-01..04-10 全部 PLAN+SUMMARY)为空;`packages-user/entry-client` 与 `packages-user/entry-data` 零改动;`git status --porcelain -- .planning/phases/04-render-adaptation/04-CONTEXT.md` **非空**(用户未提交改动仍在,D-56);`git status --porcelain -- packages packages-user src` 的条目**全部落在本计划 `files_modified` / `files_deleted` 白名单内**——若出现白名单外的条目,先逐条确认其为用户并发改动并**原样记录**(写入 SUMMARY 的「门禁时刻实测」栏),再以环境变量 `GSD_ALLOW_USER_DIRTY=1` 重跑该门禁以放行;**未出现白名单外条目时不得设置该变量**。
+ - ⑪ **无类型导入门禁**:`git grep -n -F "import type" -- packages-user/client-base/src packages-user/client-modules/src` 零命中(`dev.md`「无类型导入」)。
+ - ⑫ **CRLF 门禁**:全部新增 / 修改文件(含 `pnpm-lock.yaml`)的 `\n` 总数等于 `\r\n` 总数。
+ 另跑**计划文件范围门禁**:phase 目录内 `-PLAN.md` 恰为 `04-01`..`04-11` 共 **11** 个;`04-01`..`04-10` 的 `*-PLAN.md` / `*-SUMMARY.md` porcelain 为空;`04-11-SUMMARY.md` 在提交前已落盘。
+
+ (2) **人工代码复核(不可自动化,如实记录结论)**:抽查并逐条打开当日源码确认「源锚点确实引用了该符号 / 目标锚点确实是该声明或桶行」——台账 A 类 `client-modules/src/render/components/tip.tsx` 的 `'../use'` 与 `render/elements/index.ts` 的 `'../renderer'` / `'../map'`;台账 B 类 `render/ui/toolbar.tsx` 的两处深层导入与 `fallback/ui.ts:2`;台账 C 类 `render/index.tsx:45,46` 与 `render/utils/index.ts:1`;台账 D 类 `client-modules/src/shared.ts` 的常量集合是否与新建 `client-base/src/shared/shared.ts` 一致(逐个核对 `CELL_WIDTH` / `CELL_HEIGHT` / `CELL_SIZE` / `MAP_BLOCK_WIDTH` / `MAP_BLOCK_HEIGHT` / `MAP_WIDTH` / `MAP_HEIGHT` / `DYNAMIC_RESERVE` / `MOVING_TOLERANCE` / `DOOR_ANIMATE_INTERVAL` 的值与注释);`map/renderer.ts` 的 4 条 `?raw` 与 `map/*` 的 5 处自包引用(R4)是否逐字未动;`createElements()` 的注册体是否零改动(D-67);`client-base/src/index.ts` 与 `package.json` 的终态。把复核结论(通过 / 发现偏差及修正)写入 SUMMARY;发现偏差即回到源码或文件修正后重跑对应门禁。
+
+ (3) **写 `.planning/phases/04-render-adaptation/04-11-SUMMARY.md`**:按执行日实际结果写(元信息块:phase / plan: 11 / subsystem / tags / requires / provides / affects / tech-stack / key-files / key-decisions / actuals / metrics / status / duration / completed),并逐条记录:本 run 的实际产出(41 个新增文件 + 14 个改动的消费者文件 + `client-base/src/index.ts` / `package.json` + `pnpm-lock.yaml` + 39 个删除)、D-58..D-68 的**逐条落点表**(裁定 → 实际文件与行号 → 实际形态)、10 处新增 `@ts-expect-error` 的实际行号与原因、5 条新增依赖的实际键序、`pnpm i` 的实际执行结果、12 道门禁的实际输出、人工复核结论、`git status --porcelain -- packages packages-user src` 的**门禁时刻实测**(含 `GSD_ALLOW_USER_DIRTY` 变量是否启用与白名单外条目的逐条原样记录)、与规划日基线的差异说明、以及 `REND-01` / `REND-02` 仍为 Pending。**须如实登记的范围外已知余留(只报告、不处置)**:① 被移文件内既有的 legacy 耦合(台账 E 类的 `core.*` 约 30 处、`loading` + `Mota.require('@user/data-base')` 3 处)随文件原样搬运、未修(D-55 / D-65);② `render/index.tsx:45,46` 改指后成为两行同源 `export * from '@user/client-base';`,本步按 D-68 保留未合并;③ 5 处 `@user/client-base` 自包引用保持原文,其在包内解析 / 构建下是否成立未验证(D-68:不要求可运行);④ `render/utils/{index,saves}.ts` 因 D-60 保留断链(仅标注);⑤ 被移集合内 8 处反向耦合(`renderer` 3 + `use` 5)未接线。**不得**把未运行的检查写成通过;**不得**把后续方案 / 新命名写进 SUMMARY(一律由用户裁定)。
+
+ (4) **提交(白名单暂存,先核对再提交)**:先 `git status --short` 逐条核对;仅暂存本计划 `files_modified` / `files_deleted` 所列条目(含 41 个新增、39 个删除、14 个消费者文件、`client-base/src/index.ts`、`client-base/package.json`、`pnpm-lock.yaml`、`04-11-SUMMARY.md`,以及本计划的 `04-11-PLAN.md` 与 `.planning/ROADMAP.md` 的本次登记改动——若 ROADMAP 改动已在计划阶段提交则不复述);**不得**暂存 / 提交 `.planning/phases/04-render-adaptation/04-CONTEXT.md`、`packages-user/data-base/**` 或任何白名单外的用户并发改动;**不得**改写 / 覆盖 / 改号 `04-01`..`04-10` 的既有产物。暂存后以 `git diff --cached --name-only` 复核暂存面与白名单一致,再提交,提交信息用 `refactor(04-11): 渲染端结构性移植——通用渲染资产下沉 @user/client-base(D-58..D-68)`。
+
+ 这是同一结构迁移的收口任务(门禁 + 复核 + 簿记 + 一次白名单提交);提交本身可用 `git revert` 回退,无 one-way 决策、无公共接口行为与新命名。
+
+
+ node -e "const fs=require('fs');const q=String.fromCharCode(39);const files=[];for(const d of ['components','elements','map'])files.push(d);const need=['components/choices.tsx','components/floorSelect.tsx','components/icons.tsx','components/index.ts','components/input.tsx','components/list.tsx','components/misc.tsx','components/page.tsx','components/scroll.tsx','components/textbox.tsx','components/textboxTyper.ts','components/thumbnail.tsx','components/tip.tsx','elements/cache.ts','elements/index.ts','elements/misc.ts','elements/props.ts','elements/types.ts','map/block.ts','map/constant.ts','map/element.ts','map/index.ts','map/moving.ts','map/renderer.ts','map/status.ts','map/types.ts','map/vertex.ts','map/viewport.ts','map/extension/door.ts','map/extension/hero.ts','map/extension/index.ts','map/extension/manager.ts','map/extension/text.ts','map/extension/types.ts','map/shader/back.frag','map/shader/back.vert','map/shader/map.frag','map/shader/map.vert','layout/layout.ts','layout/index.ts','shared/shared.ts','shared/index.ts'];const bad=[];for(const f of need){if(!fs.existsSync('packages-user/client-base/src/'+f))bad.push('MISSING '+f)}if(fs.existsSync('packages-user/client-base/src/render'))bad.push('FORBIDDEN client-base/src/render');const gone=['components/choices.tsx','components/floorSelect.tsx','components/icons.tsx','components/index.ts','components/input.tsx','components/list.tsx','components/misc.tsx','components/page.tsx','components/scroll.tsx','components/textbox.tsx','components/textboxTyper.ts','components/thumbnail.tsx','components/tip.tsx','elements/cache.ts','elements/index.ts','elements/misc.ts','elements/props.ts','elements/types.ts','map/block.ts','map/constant.ts','map/element.ts','map/index.ts','map/moving.ts','map/renderer.ts','map/status.ts','map/types.ts','map/vertex.ts','map/viewport.ts','map/extension/door.ts','map/extension/hero.ts','map/extension/index.ts','map/extension/manager.ts','map/extension/text.ts','map/extension/types.ts','map/shader/back.frag','map/shader/back.vert','map/shader/map.frag','map/shader/map.vert','utils/layout.ts'];for(const f of gone){if(fs.existsSync('packages-user/client-modules/src/render/'+f))bad.push('ORIGINAL REMAINS '+f)}for(const d of ['components','elements','map']){const p='packages-user/client-modules/src/render/'+d;if(fs.existsSync(p)&&fs.readdirSync(p).length>0)bad.push('NOT EMPTY '+p)}for(const f of ['shared.ts','render/utils/index.ts','render/utils/saves.ts','render/utils/use.ts','render/renderer.ts','render/use.ts','render/index.tsx']){if(!fs.existsSync('packages-user/client-modules/src/'+f))bad.push('KEPT FILE MISSING '+f)}if(bad.length>0){console.error('FINAL MOVE GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK 42 new paths exist, 39 originals gone, kept files present')"
+ non-zero exit, 或 stderr 含 "FINAL MOVE GATE FAILED"(41 个新增 + `index.ts` / `package.json` 之外的目标缺失,或 39 个原件仍有残留,或应保留的 `shared.ts` / `render/utils/{index,saves,use}.ts` / `render` 顶层文件缺失,或违规存在 `client-base/src/render/`)
+ node -e "const fs=require('fs');const q=String.fromCharCode(39);const t=fs.readFileSync('packages-user/client-base/src/index.ts','utf8');const need=['./load','./material','./save','./types','./components','./elements','./map','./layout','./shared'];const miss=need.filter(s=>t.indexOf('export * from '+q+s+q)<0);const pkg=JSON.parse(fs.readFileSync('packages-user/client-base/package.json','utf8'));const d=pkg.dependencies||{};const dep=['@motajs/animate','@motajs/common','@motajs/render-vue','@motajs/system','@user/data-common'];const keep=['@motajs/audio','@motajs/render','@motajs/client-base','@user/data-base','@user/data-state'];const bad=miss.concat(dep.filter(x=>!d[x]).map(x=>'DEP MISSING '+x)).concat(keep.filter(x=>!d[x]).map(x=>'DEP REMOVED '+x));const lock=fs.readFileSync('pnpm-lock.yaml','utf8');const m=lock.match(/\n packages-user\/client-base:\r?\n/);if(!m)bad.push('LOCK IMPORTER MISSING');else{const rest=lock.slice(m.index+m[0].length);const end=rest.search(/\r?\n \S/);const block=end<0?rest:rest.slice(0,end);for(const x of dep)if(block.indexOf(x+':')<0)bad.push('LOCK UNSYNCED '+x)}if(bad.length>0){console.error('FINAL BARREL+DEP GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK barrel 9 specifiers; deps 5+5; lockfile synced')"
+ non-zero exit, 或 stderr 含 "FINAL BARREL+DEP GATE FAILED" / "LOCK IMPORTER MISSING" / "LOCK UNSYNCED"(5 条桶导出不全,或依赖未补 / 被误删,或 `pnpm i` 未执行 / 未同步)
+ node -e "const fs=require('fs'),{execSync}=require('child_process');const q=String.fromCharCode(39);const head=execSync('git rev-parse HEAD',{encoding:'utf8'}).trim();const ALLOW=/@ts-expect-error|'\.\.\/(\.\.\/)?shared'/;const mult=a=>{const m=new Map();for(const l of a)m.set(l,(m.get(l)||0)+1);return m};const pairs=[['packages-user/client-base/src/components','components'],['packages-user/client-base/src/elements','elements'],['packages-user/client-base/src/map','map']];const bad=[];let count=0;for(const [nd,sub] of pairs){const walk=(d,rel)=>{for(const f of fs.readdirSync(d)){const p=d+'/'+f;if(fs.statSync(p).isDirectory()){walk(p,rel+f+'/');continue}const r=rel+f;const np=nd+'/'+r,op='packages-user/client-modules/src/render/'+sub+'/'+r;count++;let orig='';try{orig=execSync('git show '+head+':'+op,{encoding:'utf8',maxBuffer:1e8})}catch(e){bad.push('NO GIT ORIGINAL '+op);continue}const a=orig.replace(/\r\n/g,'\n').split('\n'),b=fs.readFileSync(np,'utf8').replace(/\r\n/g,'\n').split('\n');const ma=mult(a),mb=mult(b);const diff=[];for(const [l,c] of mb){const dd=c-(ma.get(l)||0);for(let i=0;il.length>0&&!ALLOW.test(l));if(off.length>0)bad.push(np+' :: '+off.slice(0,3).join(' | '))}};walk(nd,'')}const src=fs.readFileSync('packages-user/client-modules/src/shared.ts','utf8').replace(/\r\n/g,'\n');const sh=fs.readFileSync('packages-user/client-base/src/shared/shared.ts','utf8').replace(/\r\n/g,'\n');const extra=sh.split('\n').filter(l=>l.trim().length>0).filter(l=>src.indexOf(l)<0);if(extra.length>0)bad.push('shared extraction new lines '+extra.length);const lay=execSync('git show '+head+':packages-user/client-modules/src/render/utils/layout.ts',{encoding:'utf8',maxBuffer:1e8}).replace(/\r\n/g,'\n');const layNow=fs.readFileSync('packages-user/client-base/src/layout/layout.ts','utf8').replace(/\r\n/g,'\n').split('\n').filter(l=>l.length>0);const layOrig=new Set(lay.split('\n'));if(layNow.filter(l=>!layOrig.has(l)).length>0)bad.push('layout.ts not verbatim');if(count!==39)bad.push('moved file count '+count+' expected 39');if(bad.length>0){console.error('FULL ALLOW-DIFF GATE FAILED :: '+bad.join(' ;; '));process.exit(1)}console.log('OK 39 move diffs limited to authorized lines; layout verbatim; shared pure extraction')"
+ non-zero exit, 或 stderr 含 "FULL ALLOW-DIFF GATE FAILED" / "NO GIT ORIGINAL"(39 个被移文件中出现授权面(`@ts-expect-error` / shared 说明符)之外的任何行差异 —— 注释被改写、逻辑被改动或自包引用被改写,违反 D-62 / R4;或 `layout/layout.ts` 非逐字副本;或 `shared/shared.ts` 引入了原件中不存在的内容)
+ node -e "const fs=require('fs'),{execSync}=require('child_process');const head=execSync('git rev-parse HEAD',{encoding:'utf8'}).trim();const mult=a=>{const m=new Map();for(const l of a)m.set(l,(m.get(l)||0)+1);return m};const files=['packages-user/client-modules/src/client.ts','packages-user/client-modules/src/types.ts','packages-user/client-modules/src/render/index.tsx','packages-user/client-modules/src/render/ui/main.tsx','packages-user/client-modules/src/render/ui/save.tsx','packages-user/client-modules/src/render/ui/settings.tsx','packages-user/client-modules/src/render/ui/statistics.tsx','packages-user/client-modules/src/render/ui/statusBar.tsx','packages-user/client-modules/src/render/ui/title.tsx','packages-user/client-modules/src/render/ui/toolbar.tsx','packages-user/client-modules/src/render/ui/viewmap.tsx','packages-user/client-modules/src/render/utils/index.ts','packages-user/client-modules/src/render/utils/saves.ts','packages-user/client-modules/src/fallback/ui.ts'];const ALLOW=/from '|export \* from|@ts-expect-error/;const bad=[];for(const f of files){const a=execSync('git show '+head+':'+f,{encoding:'utf8',maxBuffer:1e8}).replace(/\r\n/g,'\n').split('\n');const b=fs.readFileSync(f,'utf8').replace(/\r\n/g,'\n').split('\n');const ma=mult(a),mb=mult(b);const diff=[];for(const [l,c] of mb){const d=c-(ma.get(l)||0);for(let i=0;il.length>0&&!ALLOW.test(l));if(off.length>0)bad.push(f+' :: '+off.slice(0,3).join(' | '))}const zero=['packages-user/client-modules/src/render/action.ts','packages-user/client-modules/src/render/renderer.ts','packages-user/client-modules/src/render/use.ts','packages-user/client-modules/src/render/scene.ts','packages-user/client-modules/src/index.ts'];for(const f of zero){const o=execSync('git status --porcelain -- '+f,{encoding:'utf8'}).replace(/\s+$/,'');if(o.length>0)bad.push('NON-CONSUMER TOUCHED '+f+' '+o)}if(bad.length>0){console.error('FINAL CONSUMER DIFF GATE FAILED :: '+bad.join(' ;; '));process.exit(1)}console.log('OK 14 consumers import-only diffs; non-consumers untouched')"
+ non-zero exit, 或 stderr 含 "FINAL CONSUMER DIFF GATE FAILED" / "NON-CONSUMER TOUCHED"(14 个消费者文件除 import / export / 标注行外还有变动,或 `render/{action,renderer,use,scene}.ts` / `client-modules/src/index.ts` 被改动 —— 越界修改实现层实现)
+ node -e "const fs=require('fs'),{execSync}=require('child_process');const all=[];const walk=d=>{for(const f of fs.readdirSync(d)){const p=d+'/'+f;fs.statSync(p).isDirectory()?walk(p):all.push(p)}};walk('packages-user/client-base/src');let n=0;const bad=[];for(const p of all){fs.readFileSync(p,'utf8').replace(/\r\n/g,'\n').split('\n').forEach((l,i)=>{if(l.indexOf('@ts-expect-error')>=0){n++;if(!/@ts-expect-error\s+\S/.test(l))bad.push(p+':'+(i+1)+' BARE')}})}if(n!==15)bad.push('client-base annotations '+n+' expected 15');for(const f of ['packages-user/client-modules/src/render/utils/index.ts','packages-user/client-modules/src/render/utils/saves.ts']){const a=fs.readFileSync(f,'utf8').replace(/\r\n/g,'\n').split('\n').filter(l=>l.indexOf('@ts-expect-error')>=0);if(a.length!==1||!/@ts-expect-error\s+\S/.test(a[0]))bad.push(f+' annotations '+a.length)}let hits=[];for(const pat of ['@user/client-modules','client-modules','import type']){let o='',st=0;try{o=execSync('git grep -n -F '+JSON.stringify(pat)+' -- packages-user/client-base/src packages-user/client-modules/src',{encoding:'utf8',stdio:['ignore','pipe','pipe']})}catch(e){o=(e.stdout||'')+(e.stderr||'');st=e.status}if(o.replace(/\s+$/,'').length>0&&pat!=='import type')hits.push(pat+' => '+o.split(/\r?\n/).slice(0,3).join(' ; '));if(pat==='import type'&&o.replace(/\s+$/,'').length>0)hits.push('import type => '+o.split(/\r?\n/).slice(0,3).join(' ; '));if(pat!=='import type'&&o.replace(/\s+$/,'').length===0&&st!==0&&st!==1)hits.push(pat+' grep status '+st)}if(hits.length>0)bad.push('REVERSE DEP / TYPE IMPORT: '+hits.join(' || '));if(bad.length>0){console.error('FINAL ANNOTATION GATE FAILED :: '+bad.join(' ;; '));process.exit(1)}console.log('OK 15+2 annotations with reasons; no reverse dep; no import type')"
+ non-zero exit, 或 stderr 含 "FINAL ANNOTATION GATE FAILED" / "BARE" / "REVERSE DEP"(`client-base/src` 标注总数不是 15,或 `render/utils/{index,saves}.ts` 各不足 1 条带原因标注,或存在裸标注,或 `client-base` / `client-modules` 内出现对 `client-modules` 的反向引用、或出现 `import type`)
+ node -e "const fs=require('fs'),{execSync}=require('child_process');const zero=execSync('git status --porcelain -- packages-user/client-base/src/material packages-user/client-base/src/load packages packages/ src packages-user/entry-client packages-user/entry-data .planning/REQUIREMENTS.md',{encoding:'utf8'}).replace(/\s+$/,'');const bad=[];if(zero.length>0)bad.push('FORBIDDEN PATH TOUCHED '+zero);const ctx=execSync('git status --porcelain -- .planning/phases/04-render-adaptation/04-CONTEXT.md',{encoding:'utf8'}).replace(/\s+$/,'');if(ctx.length===0)bad.push('USER CONTEXT EDIT DISAPPEARED (D-56)');const allow=['packages-user/client-base/src/','packages-user/client-base/package.json','packages-user/client-modules/src/render/components/','packages-user/client-modules/src/render/elements/','packages-user/client-modules/src/render/map/','packages-user/client-modules/src/render/utils/layout.ts','packages-user/client-modules/src/render/utils/index.ts','packages-user/client-modules/src/render/utils/saves.ts','packages-user/client-modules/src/render/index.tsx','packages-user/client-modules/src/render/ui/','packages-user/client-modules/src/client.ts','packages-user/client-modules/src/types.ts','packages-user/client-modules/src/fallback/ui.ts','pnpm-lock.yaml'];const raw=execSync('git status --porcelain -- packages packages-user src',{encoding:'utf8'}).replace(/\s+$/,'');const entries=raw.length>0?raw.split(/\r?\n/):[];const outside=entries.filter(l=>{const p=l.slice(3).replace(/^\"|\"$/g,'');return !allow.some(a=>a.endsWith('/')?p.indexOf(a)===0:p===a)});if(outside.length>0&&process.env.GSD_ALLOW_USER_DIRTY!=='1'){console.error('PORCELAIN OUTSIDE PLAN WHITELIST (confirm user edits, then re-run with GSD_ALLOW_USER_DIRTY=1): '+outside.join(' ; '));process.exit(1)}if(outside.length>0)console.log('INFO recorded out-of-whitelist entries (assumed user edits, D-56): '+outside.join(' ; '));const dir='.planning/phases/04-render-adaptation';const plans=fs.readdirSync(dir).filter(f=>/-PLAN\.md$/.test(f)).sort();if(plans.length!==11||plans.filter(p=>!/^04-(0[1-9]|1[01])-PLAN\.md$/.test(p)).length>0)bad.push('PLAN FILES '+plans.join(','));const ids=['04-01','04-02','04-03','04-04','04-05','04-06','04-07','04-08','04-09','04-10'];const prot=[];for(const n of ids){prot.push(dir+'/'+n+'-PLAN.md');prot.push(dir+'/'+n+'-SUMMARY.md')}const args=prot.filter(f=>fs.existsSync(f)).map(p=>JSON.stringify(p)).join(' ');const o=execSync('git status --porcelain -- '+args,{encoding:'utf8'}).replace(/\s+$/,'');if(o.length>0)bad.push('PRIOR ARTIFACTS MODIFIED '+o);if(!fs.existsSync(dir+'/04-11-SUMMARY.md'))bad.push('MISSING 04-11 SUMMARY');if(bad.length>0){console.error('FINAL SCOPE GATE FAILED :: '+bad.join(' ;; '));process.exit(1)}console.log('OK scope: forbidden paths clean, D-56 edit present, whitelist respected, 11 plans, prior artifacts intact')"
+ non-zero exit, 或 stderr 含 "FORBIDDEN PATH TOUCHED" / "USER CONTEXT EDIT DISAPPEARED" / "PORCELAIN OUTSIDE PLAN WHITELIST" / "PLAN FILES" / "PRIOR ARTIFACTS MODIFIED" / "MISSING 04-11 SUMMARY"(`material/` / `load/` / `packages/` / `src/` / `entry-client` / `entry-data` / `REQUIREMENTS.md` 被改动,或用户的 `04-CONTEXT.md` 未提交改动消失,或 `packages` / `packages-user` / `src` 出现白名单外的条目而未按 action ⑩ 记录并显式放行,或出现 `04-12+` 计划 / 前序产物被改写,或 SUMMARY 未落盘)
+ node -e "const fs=require('fs');const files=[];const walk=d=>{for(const f of fs.readdirSync(d)){const p=d+'/'+f;fs.statSync(p).isDirectory()?walk(p):files.push(p)}};walk('packages-user/client-base/src');for(const f of ['packages-user/client-base/src/index.ts','packages-user/client-base/package.json','pnpm-lock.yaml','packages-user/client-modules/src/client.ts','packages-user/client-modules/src/types.ts','packages-user/client-modules/src/render/index.tsx','packages-user/client-modules/src/render/utils/index.ts','packages-user/client-modules/src/render/utils/saves.ts','packages-user/client-modules/src/fallback/ui.ts'])files.push(f);for(const d of ['packages-user/client-modules/src/render/ui'])for(const f of fs.readdirSync(d))files.push(d+'/'+f);const bad=[];for(const f of [...new Set(files)]){const s=fs.readFileSync(f,'utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf)bad.push(f+' ('+(lf-crlf)+' stray LF)')}if(bad.length>0){console.error('FINAL NON-CRLF '+bad.join(' ; '));process.exit(1)}console.log('OK CRLF '+new Set(files).size+' files')"
+ non-zero exit, 或 stderr 含 "FINAL NON-CRLF"(新增 / 修改文件出现非 CRLF 换行,违反 dev.md)
+
+
+ - 12 道静态门禁全部转绿:目标落位(41 文件)/ 移动已发生(39 原件消失 + 应保留文件在场)/ 桶导出(9 条)/ 依赖与 lockfile(5 + 5,importer 同步)/ 消费者改指(各说明符计数符合预期)/ 反向引用零命中 / `@ts-expect-error` 落点(15 + 2,全部带原因)/ 允许差异(39 文件仅授权面,`layout.ts` 逐字、`shared/shared.ts` 纯摘取)/ 实现层差异(14 消费者仅 import 行)/ 范围与用户改动(禁用路径零改动、`04-CONTEXT.md` 未提交改动仍在、白名单外条目已记录并显式放行)/ 无 `import type` / CRLF
+ - 计划文件范围门禁:phase 目录内 `-PLAN.md` 恰为 `04-01`..`04-11` 共 **11** 个;`04-01`..`04-10` 的既有 PLAN / SUMMARY 零改写;`04-11-SUMMARY.md` 已落盘
+ - 人工复核结论已记录(台账 A / B / C / D 四类抽样 + `shared` 常量逐项核对 + `?raw` 与自包引用逐字核对 + `createElements()` 注册体零改动 + `client-base/src/index.ts` 与 `package.json` 终态),偏差均已修正
+ - `04-11-SUMMARY.md` 如实记录:41 新增 / 39 删除 / 14 消费者改动、D-58..D-68 逐条落点表、10 处标注实际行号与原因、5 条依赖、`pnpm i` 结果、12 道门禁输出、人工复核结论、门禁时刻 porcelain 实测(含 `GSD_ALLOW_USER_DIRTY` 是否启用与白名单外条目)、规划日差异说明、`REND-01` / `REND-02` 仍 Pending,以及 5 条范围外已知余留(legacy / 两行同源星导出 / 自包引用未验证 / D-60 断链 / 8 处反向耦合未接线)
+ - 提交仅含本计划 `files_modified` / `files_deleted` 白名单 + `04-11-SUMMARY.md`(+ 本计划 `04-11-PLAN.md` 与属本次登记的 ROADMAP 改动,若适用);`git diff --cached --name-only` 已复核;未纳入任何用户并发改动;未改写 `04-01..04-10` 产物
+
+ 移植实施收口:39 文件全量允许差异比对通过(授权面之外的改动为零)、12 道静态门禁全绿、计划文件范围门禁通过、人工复核结论与 5 条范围外已知余留均如实登记、`04-11-SUMMARY.md` 落盘、按白名单暂存并提交;用户可据 SUMMARY 的裁定落点表核对 D-58..D-68 是否逐条落地,并接手 5 条余留(legacy 面 / 两行同源星导出 / 自包引用 / D-60 断链 / 8 处反向耦合接线)
+
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|----------|-------------|
+| 被移源文件 → 系统层新副本 | 39 个文件跨包移动;注释 / 逻辑不得被改写(AGENTS.md 的注释不可改 + D-62 的命名不变),差异只允许落在授权面 |
+| `client-base`(系统层)→ `client-modules`(实现层) | 分层红线:系统层不得反向依赖实现层;D-59 要求「不做反向引用」,最容易在补 import 时被违反 |
+| 用户并发改动(D-56)→ 本次 run | 用户正在改数据端;本 run 不得回滚 / 暂存 / 提交 / 修改其任何未提交改动(含 `.planning/.../04-CONTEXT.md`) |
+| `packages-user/client-base/src/material/**` → AI 工作范围 | D-42 / D-48 禁区;被移文件内 5 处 `@user/client-base` 自包引用指向 `material` 类型,改指时极易越界去动 `material/` |
+| legacy 耦合面(D-55 / D-65)→ AI 工作范围 | 被移文件内 `core.*` / `loading` / `Mota.require` 约 33 处;若顺手修即越权扩大范围 |
+| `pnpm i` → 仓库清单 | D-66 授权改写 `client-base/package.json` 与 `pnpm-lock.yaml`;不得溢出到其它 `package.json` 或引入非白名单第三方包 |
+| 公共面链路(`render/index.tsx` → `client-modules/src/index.ts` → `entry-*`)→ 外部包 | 桶断链 / 再导出链不补位会让 `@user/client-modules` 公共面收窄,影响 `entry-client` / `entry-data` |
+| `@ts-expect-error` 面 → 后续维护 | 未带原因的裸标注会让后续逐条收尾失去依据(dev.md 要求说明原因),且标注面须严格限定在 D-59 / D-60 / D-67 授权落点 |
+| 执行者 / 子代理 → 执行前置关卡 | AGENTS.md 要求第一个 block 点先汇报并等待用户回复「可以执行」;`yolo` / `auto_advance` 或子代理派发都不是放行理由(Task 0 为 `blocking-human`、`autonomous: false`) |
+| 既有 04-01..04-10 产物 → 本次编号 | phase 目录内已有 10 个增量的计划与 SUMMARY;改号 / 覆盖会破坏既有执行记录,`04-12+` 会提前分叉后续步骤 |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
+|-----------|----------|-----------|----------|-------------|-----------------|
+| T-04-11-01 | 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-11-02 | Tampering | 被移文件的内容 / 注释(`client-base/src/{components,elements,map,layout,shared}/**`) | high | mitigate | 「允许差异门禁」用多重集差把 39 个新副本与 `git show HEAD:<原路径>` 逐行比对,**只**允许授权面(`@ts-expect-error` 插入、shared 说明符改写);`layout/layout.ts` 强制逐字相同;`shared/shared.ts` 强制为原件的逐行摘取;Task 2 / Task 3 / Task 4 各跑一次 |
+| T-04-11-03 | Tampering | 分层红线(`client-base` → `client-modules`) | high | mitigate | D-59 / R2 / R3 强制「说明符原文不变 + `// @ts-expect-error`」,prohibitions 明列「不得出现 `from '@user/client-modules'`」;Task 1 / 2 / 3 / 4 均以 `git grep -F` 固定串检索 `@user/client-modules` 与 `client-modules` 于 `packages-user/client-base`,非零即失败 |
+| T-04-11-04 | Tampering | 用户并发改动(D-56) | high | mitigate | Task 1 步骤 (0a) 逐行记录 `git status --porcelain -- packages packages-user src` 基线;Task 4 的范围门禁断言白名单外条目必须逐条记录并以 `GSD_ALLOW_USER_DIRTY=1` 显式放行(禁止静默放行);另断言 `.planning/.../04-CONTEXT.md` 未提交改动仍在(消失即失败);提交以 `git diff --cached --name-only` 复核白名单 |
+| T-04-11-05 | Tampering | `material/` 禁区(D-42 / D-48) | high | mitigate | Task 1 / 2 / 3 / 4 的禁用路径门禁断言 `git status --porcelain -- packages-user/client-base/src/material packages-user/client-base/src/load packages packages/ src` 为空;prohibitions 明列;5 处自包引用按 R4「只搬不改」,不触 `material/` |
+| T-04-11-06 | Spoofing | 「零反向引用」「零遗留原件」类结论 | high | mitigate | 不以文档自述为准:门禁直接运行 `git grep -F` 固定串检索 + `fs.existsSync` 逐路径断言 + 目录内文件数断言(`render/{components,elements,map}` 必须为 0 文件),任一条不成立即 non-zero 退出 |
+| T-04-11-07 | Tampering | 依赖面(`client-base/package.json` / `pnpm-lock.yaml`) | high | mitigate | 门禁解析 `client-base/package.json` 断言「5 条新增 + 5 条原样保留」,并解析 `pnpm-lock.yaml` 的 `packages-user/client-base` importer 块断言 5 条同步;prohibitions 禁改其它 `package.json`、禁引入根级已声明的第三方包 |
+| T-04-11-08 | Elevation of Privilege | legacy 范围(D-55 / D-65) | high | mitigate | 无任何 legacy 任务 / 修复 / 待办;被移文件的 legacy 耦合随文件原样搬运(由允许差异门禁保证未被「顺手修」);SUMMARY 只登记为范围外已知余留 |
+| T-04-11-09 | Tampering | `@ts-expect-error` 面 | medium | mitigate | 门禁断言 `client-base/src/**` 标注总数恰为 15、`render/utils/{index,saves}.ts` 各 1 条,且全部匹配 `@ts-expect-error <非空原因>`;授权落点为 R2 3 + R3 5 + R9 2;prohibitions 禁授权面之外新增、禁删改既有 7 条 |
+| T-04-11-10 | Repudiation | 逐条裁定的可追溯性 | medium | mitigate | `04-11-SUMMARY.md` 必须含 D-58..D-68 的逐条落点表(裁定 → 实际文件与行号 → 实际形态)+ 10 处标注实际行号 + 12 道门禁实际输出 + 人工复核结论 + 门禁时刻 porcelain 实测;不得把未运行的检查写成通过 |
+| T-04-11-11 | Elevation of Privilege | 清点 / 迁移范围(顺手改非被移集合、提前分叉后续步骤) | medium | mitigate | 实现层差异门禁限定 14 个消费者的差异只在 import / export / 标注行,并断言 `render/{action,renderer,use,scene}.ts` / `client-modules/src/index.ts` 零改动;计划文件范围门禁禁止 `04-12+` 并断言 `04-01`..`04-10` 产物零改写 |
+| T-04-11-12 | Denial of Service | 只读子代理越权写入 | low | mitigate | 本计划的移动 / 改指工作由主执行者完成;若派子代理只允许只读回传事实,写文件一律由主执行者按门禁统一验证 |
+| T-04-11-13 | Information Disclosure | SUMMARY 与门禁输出引用的源码行与符号名 | low | accept | 记录只含符号名、文件行号、成员签名与目录结构,不含凭据 / 密钥 / 个人数据 |
+| T-04-11-SC | Tampering | npm / pip / cargo 安装面 | high | mitigate | 只执行 D-66 授权的 `pnpm i`(5 条 `workspace:*` 内部包,无远程新包、无版本区间);不安装任何其它包;若 `pnpm i` 提示需要扩大安装面即暂停并告知用户;包合法性核对不适用(全部为 monorepo 内部 workspace 包) |
+
+
+
+- **目标落位 / 移动已发生**:41 个新增文件存在于 `packages-user/client-base/src/{components,elements,map,layout,shared}/`;`files_deleted` 的 39 条逐条不存在;`client-modules/src/render/{components,elements,map}` 内零文件;`client-modules/src/shared.ts` / `render/utils/{index,saves,use}.ts` / `render/{renderer.ts,use.ts,index.tsx}` 仍在;`client-base/src/render/` 不存在。
+- **桶导出**:`client-base/src/index.ts` 含 9 条 `export * from`(原 `./load` / `./material` / `./save` / `./types` + 新 `./components` / `./elements` / `./map` / `./layout` / `./shared`)。
+- **依赖与 lockfile**:`client-base/package.json` 含 `@motajs/animate` / `@motajs/common` / `@motajs/render-vue` / `@motajs/system` / `@user/data-common` 且原 5 条保留;`pnpm-lock.yaml` 的 `packages-user/client-base` importer 块同步 5 条。
+- **消费者改指**:`'./render/map'` 0、`'./components'` 0、`'./elements'` 0、`'./layout'` 1、`'../components'` 1、`'../components/icons'` 0、`'../components/misc'` 0、`'../render/components/tip'` 0;`render/index.tsx` 出现 2 条 `export * from '@user/client-base';`;`@user/client-base` 引用数 ≥ 19。
+- **`@ts-expect-error` 落点**:`client-base/src/**` 内共 15 条、`render/utils/{index,saves}.ts` 各 1 条,全部带非空原因;新增仅 R2 3 + R3 5 + R9 2;既有 7 条逐字保留。
+- **允许差异**:39 个被移文件相对 `git show :<原路径>` 的差异**只**出现在授权面;`layout/layout.ts` 逐字相同;`shared/shared.ts` 为 `client-modules/src/shared.ts` 的逐行摘取;14 个消费者文件的差异只在 import / export / 标注行。
+- **分层红线**:`git grep -n -F "@user/client-modules" -- packages-user/client-base` 与 `git grep -n -F "client-modules" -- packages-user/client-base` 均零命中。
+- **范围与用户改动**:`material/` / `load/` / `packages/` / `src/` / `entry-client` / `entry-data` / `.planning/REQUIREMENTS.md` 零改动;`04-CONTEXT.md` 的未提交改动仍在;`packages` / `packages-user` / `src` 的 porcelain 条目全部落在本计划白名单内(白名单外条目必须逐条记录并以 `GSD_ALLOW_USER_DIRTY=1` 显式放行)。
+- **计划文件范围**:phase 目录内 `-PLAN.md` 恰为 `04-01`..`04-11` 共 11 个;`04-01`..`04-10` 的 PLAN / SUMMARY porcelain 为空;`04-11-SUMMARY.md` 落盘。
+- **CRLF**:全部新增 / 修改文件的 `\n` 总数等于 `\r\n` 总数。
+- **不可自动化项的诚实说明(D-68)**:本步明确**没有「保证能运行」的要求**,也**不把** `pnpm check:type` / `pnpm build` / 全仓或范围内 TS 诊断数当作门禁或验收项;不存在其它可运行门禁,不得以任何无法运行的门禁冒充通过。
+
+
+
+
+## Artifacts this phase produces
+
+| Artifact | Kind | Purpose |
+|----------|------|---------|
+| `packages-user/client-base/src/components/**`(13) | 生产代码(新增) | 通用渲染组件从实现层移入系统层(D-51 / D-52 / D-53);`'../renderer'` / `'../use'` 耦合以 D-59 / D-68 的 8 处 `// @ts-expect-error` 暂记;barrel 12 条 `export *` 逐字保留 |
+| `packages-user/client-base/src/elements/**`(5) | 生产代码(新增) | 渲染元素从实现层移入系统层;`createElements()` 标签 / 元件注册体逐字移植、逻辑零改动(D-67) |
+| `packages-user/client-base/src/map/**`(20) | 生产代码(新增) | 地图渲染(含 `extension/` 6 + `shader/` 4)从实现层移入系统层;shared 说明符按 D-58 改指 4 处;`?raw` 与集合内部相对路径不变(D-64);5 处自包引用只搬不改(R4) |
+| `packages-user/client-base/src/layout/{layout.ts,index.ts}` | 生产代码(新增) | tracer 切片对象:`render/utils/layout.ts` 的逐字副本 + 新桶(D-61) |
+| `packages-user/client-base/src/shared/{shared.ts,index.ts}` | 生产代码(新增) | 从 `client-modules/src/shared.ts` 摘取的 10 个被移 `map` 文件所需常量(纯摘取、注释逐字)+ 新桶(D-58) |
+| `packages-user/client-base/src/index.ts` | 生产代码(修改) | 新增 5 条桶导出,使被移资产与新建桶 / shared 可从 `@user/client-base` 直接消费(D-63) |
+| `packages-user/client-base/package.json` | 生产清单(修改) | 新增 5 条 `workspace:*` 依赖,承托被移集合的外部依赖面(D-66) |
+| `pnpm-lock.yaml` | 根清单(经 `pnpm i` 同步) | D-66 的 `pnpm i` 证据;`packages-user/client-base` importer 块同步 5 条依赖 |
+| `packages-user/client-modules/src/{client.ts,types.ts}` | 生产代码(修改) | `'./render/map'` 改指 `@user/client-base`(D-63) |
+| `packages-user/client-modules/src/render/index.tsx` | 生产代码(修改) | `:5` / `:45` / `:46` 说明符改指 `@user/client-base`(D-63);`:51` 与 `'../shared'` 不动 |
+| `packages-user/client-modules/src/render/ui/**`(8 文件 11 处) | 生产代码(修改) | 浅层与深层(`components/icons` / `components/misc`)导入改指 `@user/client-base`(D-63) |
+| `packages-user/client-modules/src/render/utils/{index.ts,saves.ts}` | 生产代码(修改) | 指向被移内容的 2 处引用按 D-60 加带原因 `// @ts-expect-error`(不反向引用、不改指) |
+| `packages-user/client-modules/src/fallback/ui.ts` | 生产代码(修改) | 深层导入 `'../render/components/tip'` 改指 `@user/client-base`(D-63) |
+| `packages-user/client-modules/src/render/{components,elements,map}/**`(39)+ `render/utils/layout.ts` | 生产代码(删除) | MOVE 语义的另一半:原件删除,避免同名死副本与两套真相 |
+| `.planning/phases/04-render-adaptation/04-11-PLAN.md` | 计划簿记 | 本计划(编号 04-11;`wave: 10`;`depends_on` 04-01..04-10;`autonomous: false`) |
+| `.planning/phases/04-render-adaptation/04-11-SUMMARY.md` | 执行簿记 | D-58..D-68 逐条落点表、10 处标注实际行号与原因、5 条依赖、`pnpm i` 结果、12 道门禁输出、人工复核结论、门禁时刻 porcelain 实测、规划日差异说明、`REND-01` / `REND-02` 仍 Pending、5 条范围外已知余留 |
+| `.planning/ROADMAP.md`(Phase 4 的 Wave 10 登记 + register note) | 计划簿记 | 登记第十一个增量 `04-11`(第二步·移植实施)与执行前置关卡要求 |
+| 门禁结果(会话内可复跑) | 验证证据 | 目标落位 / 移动已发生 / 桶导出 / 依赖与 lockfile / 消费者改指 / 反向引用零命中 / `@ts-expect-error` 落点 / 允许差异(39 文件)/ 实现层差异 / 范围与用户改动 / 无 `import type` / CRLF 共 12 道 + 计划文件范围门禁 |
+| 显式**不**产出 | 边界声明 | 任何 legacy 修复(D-55 / D-65)、任何 `material/` 或加载面改动(D-42 / D-48)、任何对 `render/renderer` / `render/use.ts` / `render/utils` 的解耦或反向引用(D-59 / D-60)、任何命名变更(D-62)、任何 `?raw` 处理(D-64)、任何其它 `package.json` 或依赖改动、任何测试文件、任何 `04-12+` 计划文件、任何以「可运行 / 类型干净」为条件的验收项(D-68) |
+