diff --git a/.planning/STATE.md b/.planning/STATE.md index bd15d8a..ec16e11 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -5,14 +5,14 @@ current_phase: 04 current_phase_name: 渲染适配与双布局 status: executing stopped_at: Completed 04-10-PLAN.md(渲染端结构性重构·第一步只读影响清点:交付 04-STRUCTURE-MIGRATION-IMPACT.md,39 文件移集 / A9 B9 C8 D10 E6 F10;生产代码零改动、零命名变更;REND-01/REND-02 保持 Pending,第二步未规划) -last_updated: "2026-09-24T07:20:00.000Z" +last_updated: "2026-09-24T08:33:34.323Z" last_activity: 2026-09-24 -last_activity_desc: Completed 04-10-PLAN.md(渲染端结构性重构·第一步只读清点) -state_head: edf0a2d974e1226d274c05a2950f31d42cf2817a +last_activity_desc: Planned 04-11-PLAN.md(渲染端结构性重构·第二步移植实施;通过 checker) +state_head: debf2a67e3cbbd265f45f4d7b9430b1f9b78f3fd progress: total_phases: 7 completed_phases: 1 - total_plans: 81 + total_plans: 82 completed_plans: 80 milestone_name: milestone --- @@ -28,10 +28,10 @@ See: .planning/PROJECT.md (updated 2026-09-17) ## Current Position -Phase: 04 (渲染适配与双布局) — IN PROGRESS -Plan: 10/10 已执行(04-01..04-10) -Status: 04-10 完成(结构性重构第一步只读清点:台账交付,含分层/循环依赖风险与断链点);Phase 4 整体仍未完成 — 第二步(移植实施)与 REND-01/REND-02 未做 -Last activity: 2026-09-24 — Completed 04-10-PLAN.md(渲染端结构性重构·第一步只读清点) +Phase: 04 (渲染适配与双布局) — READY TO EXECUTE +Plan: 10/11 已执行(04-01..04-10);04-11(渲染端结构性重构·第二步移植实施)已规划待执行(Wave 10) +Status: 04-11 已规划并通过 checker(0 blocker / 0 warning / 1 info-advisory);待用户回复「可以执行」后执行;Phase 4 整体仍未完成 — REND-01/REND-02 保持 Pending +Last activity: 2026-09-24 — Planned 04-11-PLAN.md(渲染端结构性重构·第二步移植实施) Progress: [█████░░░░░] 50% diff --git a/.planning/phases/04-render-adaptation/04-11-PLAN.md b/.planning/phases/04-render-adaptation/04-11-PLAN.md index 10159bd..a812b68 100644 --- a/.planning/phases/04-render-adaptation/04-11-PLAN.md +++ b/.planning/phases/04-render-adaptation/04-11-PLAN.md @@ -15,7 +15,7 @@ depends_on: - 04-09 - 04-10 files_modified: - # ---- client-base 系统层新增(41)---- + # ---- client-base 系统层新增(42)---- - packages-user/client-base/src/components/choices.tsx - packages-user/client-base/src/components/floorSelect.tsx - packages-user/client-base/src/components/icons.tsx @@ -270,7 +270,7 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· ## 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` 一份计划,故以任务切分替代计划切分,并保持每个任务落在单代理上下文预算内。 +`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` 一份计划,故以任务切分替代计划切分,并保持每个任务落在单代理上下文预算内。**已知并显式接受超预算(over_budget=true):** 本计划估算 **130k tokens**,高于单计划 smart-zone 预算(100k,约 **1.30x**)。这是**刻意接受**的取舍而非疏漏——用户明确要求把整个移植一次做完(39 文件移动 + 5 条桶导出 + 14 个消费者改指 + 5 条依赖与 `pnpm i` + 10 处标注 + 收口),并**效率优先、细节不纠结**(D-68);拆成多份计划会引入跨计划的「半移」临时态与额外往返。故本计划以单份 `04-11-PLAN.md`(`wave: 10`)交付,超预算在此登记为**已记录的选择**。 @@ -339,9 +339,9 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· ⑦ **范围与硬边界(须原样呈报)**:**零触碰** —— `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 诊断数作为门禁或验收项。 + ⑧ **验证方式(须原样呈报,并声明本步无运行时要求)**: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+`。 + ⑬ **编号与 wave(须原样告知)**:本计划为 **04-11**(第十一个增量),`wave: 10`,`depends_on` 列 `04-01`..`04-10`;phase 目录内既有 `04-01`..`04-10` 的 `*-PLAN.md` 与 `*-SUMMARY.md` **一律不得改写 / 覆盖 / 改号**,也不得产出 `04-12+`。另须告知:本计划估算 **130k tokens**(约 **1.30x** 单计划 smart-zone 预算,`over_budget=true`),已按「效率优先、一次做完整个移植」(D-68)**刻意接受**超预算、不因此拆分(见 Estimate Basis 的显式登记)。 ⑭ **盖章效力(须原样告知)**:本计划为**代码改动型**增量,将实际移动 39 个文件、改 14 个实现层文件的引用行、改 1 个 `client-base` 的 `index.ts` 与 `package.json`、执行 1 次 `pnpm i`,并删除 `client-modules/src/render/` 下 39 个原件。所有改动均可用 `git revert` 整体回退。 @@ -380,7 +380,7 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· - 用户回复中明确包含「可以执行」四个字 - 汇报内容涵盖:要解决的问题(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+` + - 汇报中已原样告知 **编号与 wave**(04-11 / wave 10 / `depends_on` 04-01..04-10)以及既有 `04-01`..`04-10` 产物不得改写 / 覆盖 / 改号、不得产出 `04-12+`,并告知估算 130k tokens(约 1.30x 预算、已刻意接受超预算) - 汇报中已原样告知**验证方式**(目标落位 / 移动已发生 / 桶导出 / 依赖与 lockfile / 消费者改指 / 反向引用零命中 / `@ts-expect-error` 落点 / 允许差异 / 范围与用户改动 / CRLF / 人工复核),并声明**不使用** `pnpm check:type` / `pnpm build` / TS 诊断数类门禁 - 汇报中已原样告知**关卡性质**:执行者/子代理必须停下等待,不因触发方式或 `yolo` / `auto_advance` 模式自动放行 @@ -411,7 +411,7 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· (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`。 + (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 条。**`pnpm i` 可能以 LF 重写 `pnpm-lock.yaml`**(本仓库 `core.autocrlf=true`,既有 `pnpm-lock.yaml` 为 CRLF),故 `pnpm i` 后须**在不改变文件内容的前提下**把 `pnpm-lock.yaml` 规范化回 CRLF(可用 node 一行:读入 → 若存在孤立 LF 则先 `\r\n`→`\n` 再 `\n`→`\r\n` 后写回),否则本任务 action (7) 的 CRLF 门禁会失败;`core.autocrlf=true` 下该规范化不会被 git 记为语义改动。**不得**改动任何其它 `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`。 @@ -428,7 +428,7 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· 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')" + 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(String.fromCharCode(39)+x+String.fromCharCode(39)+':')<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 加带原因的标注,或标注无原因,或另两行被改动) @@ -508,16 +508,16 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· 本任务是同一结构迁移的第二切片(再移两个目录 + 再补两条桶 + 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')" + 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=[];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');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!==14){bad.push('components+elements total annotations '+total+' expected 14')}if(bad.length>0){console.error('ANNOTATION GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK annotations: 14 total (6 pre-existing + 8 new), all with reasons')" + non-zero exit, 或 stderr 含 "ANNOTATION GATE FAILED" / "BARE"(`components` / `elements` 下的 `@ts-expect-error` 数量与「既有 6 + 新增 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!==3||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` / 3 处 `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),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)}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:5` 的 `'./elements'` 未改指(`'./elements'` 应自 2 处降为 1 处,仅剩 `:46` 的 `export * from './elements';` 留待 Task 3),或改指总数不足 —— 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')" @@ -529,11 +529,11 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· - `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 新增)且每行均带非空原因 + - 被移文件内**既有 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` 两目录内标注**总数恰为 14**(6 既有 + 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 只移植不接线) + - `elements/index.ts` 的 `createElements()` 与 3 次 `registerElement` + 3 处 `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** + - `'../components'` 在 `packages-user/client-modules/src` 内仅剩 `render/utils/saves.ts` 的 **1** 处;`'../components/icons'` / `'../components/misc'` / `'../render/components/tip'` 均为 **0** 处;`'./elements'` 为 **1** 处(`render/index.tsx:5` 已改指,`:46` 的 `export * from './elements';` 留待 Task 3);`@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;未触碰本任务范围外文件 @@ -551,7 +551,7 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· - 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/index.tsx:44-54(`:45` `export * from './components';`、`:46` `export * from './elements';` 改指为 `export * from '@user/client-base';`;`:51` 的 `'./renderer'` 与 `:53` 的 `'../shared'` 不动) - 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 <原因>` 的书写先例) @@ -576,7 +576,7 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· - `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';`)**不得**触碰。 + - `render/index.tsx:51`(`export * from './renderer';`)与 `render/index.tsx:53`(`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/`。 @@ -585,8 +585,8 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· 本任务是同一结构迁移的第三切片(移入最大目录 + 补最后一条桶 + 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 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';const hasFile=p=>fs.existsSync(p)&&fs.readdirSync(p,{withFileTypes:true}).some(e=>e.isDirectory()?hasFile(p+'/'+e.name):true);if(hasFile(d))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 (recursive-empty)')" + 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')" @@ -605,7 +605,7 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· 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/` 内**零文件** + - `packages-user/client-base/src/map/` 含 **20** 个文件(顶层 10 + `extension/` 6 + `shader/` 4,含 4 个二进制等价文本 shader);`packages-user/client-modules/src/render/map/` 内**递归零文件**(允许残留空的 `extension/` / `shader/` 目录) - 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 行原文保留 @@ -621,7 +621,7 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· 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 + packages-user/client-base/**(42 新增 + 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 落盘、白名单提交) @@ -632,8 +632,8 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· (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/{components,elements,map,layout,shared}/` 下 42 个文件存在(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。 @@ -648,16 +648,16 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· (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(一律由用户裁定)。 + (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 的实际产出(42 个新增文件 + 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)`。 + (4) **提交(白名单暂存,先核对再提交)**:先 `git status --short` 逐条核对;仅暂存本计划 `files_modified` / `files_deleted` 所列条目(含 42 个新增、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')" + 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)}const hasFile=p=>fs.existsSync(p)&&fs.readdirSync(p,{withFileTypes:true}).some(e=>e.isDirectory()?hasFile(p+'/'+e.name):true);for(const d of ['components','elements','map']){const p='packages-user/client-modules/src/render/'+d;if(hasFile(p))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"(42 个新增 + `index.ts` / `package.json` 之外的目标缺失,或 39 个原件仍有残留(含 `render/map/` 递归零文件校验),或应保留的 `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(String.fromCharCode(39)+x+String.fromCharCode(39)+':')<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` 引入了原件中不存在的内容) @@ -671,10 +671,10 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· 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 + - 12 道静态门禁全部转绿:目标落位(42 文件)/ 移动已发生(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 处反向耦合未接线) + - `04-11-SUMMARY.md` 如实记录:42 新增 / 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 处反向耦合接线) @@ -719,7 +719,7 @@ Phase 4 第十一个增量(用户裁定 2026-09-24 · D-58..D-68 **第二步· -- **目标落位 / 移动已发生**: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/` 不存在。 +- **目标落位 / 移动已发生**:42 个新增文件存在于 `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。 diff --git a/.planning/state.json b/.planning/state.json index 6142424..7aaba9a 100644 --- a/.planning/state.json +++ b/.planning/state.json @@ -44,5 +44,5 @@ "label": "Advance to the next step", "reason": "Phase 04 of 7 · executing" }, - "updated_at": "2026-09-24T06:31:01.495Z" + "updated_at": "2026-09-24T08:33:34.410Z" }