docs(04): create phase plan for read-only render/data interface audit

This commit is contained in:
unanmed 2026-09-18 09:40:38 +08:00
parent 8d6928294a
commit f480ec704c
2 changed files with 299 additions and 2 deletions

View File

@ -181,7 +181,12 @@ Plans:
3. 移动端(窄屏)布局下,同一场景正常显示且可操作
4. 数据端与渲染端保持双端分离——数据端无 DOM仍可在 Node 环境跑回放验证
**Plans**: TBD
**Plans**: 1 plan第一步·只读对账本阶段增量规划后续适配实施与双布局待对账结果出来后另行规划 — 04-CONTEXT D-01/D-02
Plans:
- [ ] 04-01-PLAN.md — 渲染端 ↔ 数据端接口对账(只读清点,产出 `04-RENDER-INTERFACE-AUDIT.md`;按 ① 错配 / ② 数据端缺失 / ③ 多余旧路径 三类登记)
**UI hint**: yes
### Phase 5: Legacy 移植
@ -375,7 +380,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7
| 1. 事件系统 | 13/13 | In Progress| |
| 2. 寻路系统 | 5/5 | In Progress| |
| 3. 数据端完成 | 19/19 | Complete | 2026-09-12 |
| 4. 渲染适配与双布局 | 0/TBD | Not started | - |
| 4. 渲染适配与双布局 | 0/1 | In Progress| - |
| 5. Legacy 移植 | 0/TBD | Not started | - |
| 6. 单元测试 | 18/18 | In Progress| |
| 7. 数据端缺陷修复 | 15/16 | 暂缓 (Deferred) | - |

View File

@ -0,0 +1,292 @@
---
phase: 04-render-adaptation
plan: 1
type: execute
wave: 1
depends_on: []
files_modified:
- .planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md
autonomous: true
requirements:
- REND-01
- REND-02
estimate:
tokens: 120000
raw_tokens: 120000
tasks: 3
confidence: low
must_haves:
truths:
- "`.planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md` 存在,且含 `07-LOADSTATE-AUDIT.md` 式骨架:「背景 / 方法 / 发现 / 触发序列(举例) / 判定为「匹配」的同类边界 / 未能从阅读确定(未猜测) / 处置」七段,加上独立成节的「② 数据端缺失接口」,共 8 个二级标题"
- "发现表每条记录精确到「接口名 + 所属文件:行 + 分类 + 问题描述 + 影响 + 置信 + 依据」D-07分类只取 ① 错配(`#04-01-M-N`)或 ③ 多余旧路径(`#04-01-L-N`),② 数据端缺失另走独立节D-08"
- "「② 数据端缺失接口」为独立节,每条以 `### #04-01-G-N标题严重度` 登记并含「现状 / 期望」两段;节末有固定结论行(确认 N 项,或明示本步未确认缺失接口)"
- "每条 `#04-01-M-*` / `#04-01-L-*` 记录的「所属文件:行」锚点指向 `packages-user/client-base``packages-user/client-modules` 的 tracked 源码行;每条 `#04-01-M-*` 在「依据」列引用至少一个数据端基准文件(`data-common` / `data-base` / `data-system` / `data-state``src/*.ts`),支持 D-05「以接口签名为准」"
- "已确认走新接口的同类读取(`core.material.*` 渲染侧素材族、`hook` / `loading` 订阅等)登记在「判定为『匹配』的同类边界」,未被误报为错配;静态阅读无法定论的调用链登记在「未能从阅读确定(未猜测)」而非写成缺陷"
- "被查两个渲染端包零改动:`git status --porcelain -- packages-user/client-base packages-user/client-modules` 为空;除本审计文档与 `04-01-PLAN.md` 外不新增任何计划文件(后续适配实施与双布局未规划、未实施)"
- "`## 处置` 登记:本步为只读清点、无生产代码改动;后续步骤由用户在对账结果出来后决定如何拆分;双布局既有资产(`render/use.ts` 的 `Orientation`/`onOrientationChange`、`shared.ts` 布局常量)仅登记为后续起点,不展开实施"
artifacts:
- path: .planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md
provides: "渲染端 ↔ 数据端接口对账账本8 段骨架 + 三分类记录表 + 独立的数据端缺失接口节 + 匹配边界 + 未确定段 + 处置结论;供用户决定后续适配与双布局如何拆分"
key_links:
- "发现表「所属文件:行」列 ↔ `packages-user/client-{base,modules}/src/**` 的 tracked 源码行(经 `git ls-files` 验证,不得指向 gitignored 镜像路径)"
- "每条 `#04-01-M-*` 的「依据」列 ↔ 数据端 `types.ts` / `core.ts` / `ins.ts` 的现行接口签名D-04/D-05接口名必须能在基准文件中逐字命中"
- "「判定为『匹配』的同类边界」段 ↔ 04-PATTERNS.md §4 的 A3 组判据(`core.*` 读取先判「数据端状态」还是「渲染侧素材」),防止把素材读成数据端错配"
- "「未能从阅读确定(未猜测)」段 ↔ 07-LOADSTATE-AUDIT.md:48-53 的证据纪律(静态阅读不能定论的链路只登记、不判定)"
- "`## 处置` ↔ 后续步骤的用户决策D-01本 run 只交付清点,适配实施与双布局的拆分由用户在对账结果出来后决定"
assumptions:
- "**FLAGGED ASSUMPTIONspecless fallback— REND-01 edge unresolved** 未提供 REND-01渲染端适配新数据层接口的边界/验收判据,探测结果为 `unclassified / unresolved`。本计划**不发明** REND-01 的验收阈值;它只交付供 REND-01 实施阶段消费的只读差异账本。用户须在规划适配工作前补齐 REND-01 的边界判据。"
- "**FLAGGED ASSUMPTIONspecless fallback— REND-02 edge unresolved** 未提供 REND-02移动端与桌面端双布局的边界/验收判据,探测结果为 `unclassified / unresolved`。本计划**不发明** REND-02 的验收阈值;仅在对账文档「处置」段登记既有双布局资产为后续起点,布局实现不在本 run。用户须在规划双布局前补齐 REND-02 的边界判据。"
- "数据端接口签名在本次 `04-PATTERNS.md` 之后可能仍被用户并行修改实现但不改签名D-05对账以基准文件的**现行签名**为准,若执行时发现签名已变,以执行当日基准文件为准并在「未能从阅读确定」记录该变更。"
- "审计文档为新增 untracked 文件,`04-PATTERNS.md` 亦为尚未提交的规划产物(`git status` 显示 `??`);本计划的门禁只断言「无新增计划文件」与「被查包零改动」,不对 `.planning` 下其它规划产物的提交状态做断言。"
prohibitions:
- "不得修改或新建 `packages-user/client-base` / `packages-user/client-modules` 下的任何文件——本步是只读清点D-02/D-09。"
- "不得规划、设计或实施渲染适配与双布局工作——本 run 只交付只读对账D-01/D-02不得新增任何后续步骤的计划文件。"
- "不得写入任何缺少 `file:line` 锚点的记录;静态阅读无法定论的调用链只能进「未能从阅读确定(未猜测)」段,不得写成缺陷或错配。"
- "不得把纯渲染侧读取(如 `core.material.*`、`core.icons.*` 等素材/全局)登记为数据端错配;它们属于「判定为『匹配』的同类边界」。"
- "不得把被查范围扩大到其它包(`packages/**`、`entry-client`、`legacy-plugin-client`、`legacy-plugin-data`、`data-fallback`,以及作为基准的 `data-*` 包本体D-03。"
---
<objective>
Phase 4 第一步增量规划D-01/D-02对渲染端与数据端做**只读接口对账**,产出 `.planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md`,清点所有「渲染端实现 ↔ 数据端现行接口」不匹配项,供用户后续决定剩余工作(适配实施 + 移动端/桌面端双布局)如何拆分。
**本 run 只做清点,不改任何代码。** 被查对象限 `packages-user/client-base``packages-user/client-modules`D-03数据端 `data-common` / `data-base` / `data-system` / `data-state` 仅作接口基准D-04对账以接口签名为准、不受用户并行修改数据端**实现**的影响D-05
三类固定分类D-06① 错配(`#04-01-M-N` ② 数据端缺失(`#04-01-G-N`独立成节D-08 ③ 多余旧路径(`#04-01-L-N`)。每条记录精确到接口名,含所属文件 `file:line`、问题描述、影响D-07
Purpose: 阶段 4 无法一次规划完毕。先拿到第一手对账账本,才能把「适配实施」与「双布局实施」拆成可执行、可验证的后续计划,避免在未核实接口差异的前提下直接改动渲染端。
Output: `04-RENDER-INTERFACE-AUDIT.md`(本 run 唯一新增产物;零生产代码改动)。
**UI 说明**:本 run 为只读对账,不产出任何 UI/前端实现产物,不适用 UI-SPEC`check ui-plan-gate` 因 ROADMAP Phase 4 含「移动端/桌面端布局」词汇而结构性命中属预期。Phase 4 的移动端/桌面端双布局 UI 工作属 `## Deferred Ideas`,其 UI-SPEC 应在规划双布局时单独产出;本 run 只在审计文档「处置」段登记既有双布局资产为其起点。
**Artifacts this phase produces本计划产出物**
- **文件**`.planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md`(新建,唯一写入目标)。
- **章节**8 个二级标题,下游验证按标题名核对):`## 背景` / `## 方法` / `## 发现` / `## ② 数据端缺失接口`独立成节D-08 / `## 触发序列(举例)` / `## 判定为「匹配」的同类边界` / `## 未能从阅读确定(未猜测)` / `## 处置`
- **元信息块**(标题下 4 行,对照 07-LOADSTATE-AUDIT.md:3-6`**日期:**` / `**范围:**`(被查 client-base + client-modulesdata-* 为接口基准) / `**性质:**`(只读清点,未修改任何代码) / `**接口基准:**`(各包 `types.ts` / `core.ts` / `ins.ts` 现行签名)。
- **发现表列**(固定顺序):`| ID | 接口名 | 所属文件:行 | 分类 | 问题描述 | 影响 | 置信 | 依据 |`。
- **记录 ID 前缀**(下游按前缀检索):
- `#04-01-M-N` —— ① 错配N 从 1 递增)
- `#04-01-L-N` —— ③ 多余旧路径
- `#04-01-G-N` —— ② 数据端缺失,独立节内以 `### #04-01-G-N标题严重度` + `- **现状**` / `- **期望**` 登记
- **② 节结论行**(固定写法,必须出现其一):`**②节结论:** 本步确认 N 项数据端缺失接口(见上)` 或 `**②节结论:** 本步未确认数据端缺失接口;相关不确定项见「未能从阅读确定(未猜测)」`
- **`## 处置` 必备内容**:本步为只读清点、无生产代码改动;后续适配实施与移动端/桌面端双布局**未在本 run 规划**,由用户在对账结果出来后决定拆分;双布局既有资产(`render/use.ts` 的 `Orientation`/`onOrientationChange`、`shared.ts` 的 `MAP_BLOCK_WIDTH`/`STATUS_BAR_WIDTH`/`MAIN_WIDTH` 等布局常量)仅登记为后续起点,不展开。
## Source Audit多源覆盖审计
| SOURCE | ID | Feature/Requirement | Plan | Status | Notes |
|--------|----|---------------------|------|--------|-------|
| GOAL | — | Phase 4「渲染端通过新数据层接口驱动并同时支持移动端与桌面端布局」 | 04-01 | COVERED仅第一步 | D-01 增量规划:本 run 只规划第一步(只读对账);适配实施与双布局属 CONTEXT.md `## Deferred Ideas`,待对账结果出来后另行规划 |
| REQ | REND-01 | 渲染端适配新数据层接口 | 04-01 | COVERED仅清点 | 本 run 交付 REND-01 的差异账本不实施适配REND-01 验收边界未提供 → 见 assumptions 的 FLAGGED ASSUMPTION |
| REQ | REND-02 | 渲染端同时支持移动端与桌面端布局 | 04-01 | COVERED仅登记资产 | 本 run 只登记双布局既有资产为后续起点04-PATTERNS.md §7布局实现属 Deferred |
| CONTEXT | D-01 | 增量规划,本次只锁定第一步 | 04-01 | COVERED | 04-01 即第一步;不产出后续步骤计划 |
| CONTEXT | D-02 | 交付物为审计文档;该步只读 | 04-01 | COVERED | `files_modified` 仅审计文档;只读门禁:`git status --porcelain -- packages-user/client-base packages-user/client-modules` 为空 |
| CONTEXT | D-03 | 被查对象限 client-base + client-modules | 04-01 | COVERED | 范围外路径不登记为被查项 |
| CONTEXT | D-04 | data-* 仅作接口基准 | 04-01 | COVERED | 基准文件为各包 `types.ts` / `core.ts` / `ins.ts` 等 |
| CONTEXT | D-05 | 以接口为准,不受实现变动影响 | 04-01 | COVERED | 每条 `#04-01-M-*` 的「依据」列须引用数据端基准文件 |
| CONTEXT | D-06 | 三类划分 | 04-01 | COVERED | 三个 ID 前缀一一对应 |
| CONTEXT | D-07 | 精确到接口名 + 文件 + 描述 + 影响 | 04-01 | COVERED | 发现表列固定;门禁要求每条含 `file:line` |
| CONTEXT | D-08 | 单文档分节;缺失接口独立成节 | 04-01 | COVERED | `## ② 数据端缺失接口` 独立节 + 固定结论行 |
| CONTEXT | D-09 | 允许只读子代理 | 04-01 | COVERED | Task 2/3 允许派只读子代理分派 A1..A7 扫描组,仅回传文本,不得写文件 |
| RESEARCH | — | (本阶段无 RESEARCH.md | — | N/A | Phase 4 未产出 RESEARCH.md无研究项需覆盖 |
</objective>
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/REQUIREMENTS.md
@.planning/phases/04-render-adaptation/04-CONTEXT.md
@.planning/phases/04-render-adaptation/04-PATTERNS.md
@.planning/phases/03-data-completion/03-CONTEXT.md
@.planning/phases/07-data-fixes/07-LOADSTATE-AUDIT.md
@.planning/phases/06-unit-tests/06-TEST-FINDINGS.md
@.planning/phases/06-unit-tests/06-COVERAGE-GAPS.md
@.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md
@AGENTS.md
@dev.md
</context>
<tasks>
<task type="tracer">
<name>Task 1tracer审计文档骨架端到端贯通 —— 8 段结构 + 三分类各一条实测记录 + 结构/ID 门禁转绿</name>
<files>.planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md</files>
<read_first>
- .planning/phases/07-data-fixes/07-LOADSTATE-AUDIT.md全文件章节骨架、方法段、「触发序列」、「判定为安全/匹配的同类边界」、「未能从阅读确定(未猜测)」、处置段写法)
- .planning/phases/06-unit-tests/06-TEST-FINDINGS.md:7-18字段定义表与 :24-30分节表格写法
- .planning/phases/06-unit-tests/06-COVERAGE-GAPS.md:10-13`### G-xx标题严重度` + 现状 / 期望 骨架)
- .planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md:3-5「接口事实源」声明块写法
- .planning/phases/04-render-adaptation/04-PATTERNS.md§1 章节骨架表与发现表字段、§4 A3 锚点表、§5 A4 新接口对照面)
- .planning/phases/04-render-adaptation/04-CONTEXT.mdD-01..D-09、Deferred Ideas
- packages-user/data-state/src/types.tsICoreState 基准,逐字比对用)
- packages-user/client-base/src/types.ts`IClientBase extends ICoreState` 桥头)
- packages-user/client-modules/src/action/move.ts:1-6`// @ts-expect-error 需要重构` + `import { HeroMover, IMoveController } from '@user/data-state'`
- packages-user/client-modules/src/render/ui/main.tsx:28-30,99-128新旧接口同文件并存
- packages-user/client-modules/src/render/map/renderer.ts:33`IGameMap` / `IGameMapHooks` / `IMapLayer` 直连)
- dev.md「双端分离」`@user/client-base` 为系统层、`@user/client-modules` 为实现层)
</read_first>
<action>
(1) 新建 `04-RENDER-INTERFACE-AUDIT.md`,先写标题与 4 行元信息块(`**日期:** 2026-09-18``**范围:**` 被查 `packages-user/client-base` + `packages-user/client-modules`、`data-*` 为接口基准;`**性质:** 只读清点,未修改任何代码``**接口基准:**` 各包 `types.ts` / `core.ts` / `ins.ts` 的现行签名D-04/D-05
(2) 写 `## 背景`REND-01 / REND-02、D-01 增量规划、本次只清点不实施、以及「对账以接口为准、不受实现变动影响」的理由。
(3) 写 `## 方法`:枚举面 = 04-PATTERNS.md 的 A1..A7 七组;判定口径 = 每条 `core.*` 读取先判「读的是数据端状态」还是「渲染侧素材/全局」;证据纪律 = 每条含 `file:line`、静态阅读不能定论者进「未能从阅读确定」;只读约束;并**实际执行** `git status --porcelain -- packages-user/client-base packages-user/client-modules`把输出预期为空记入本节作为「扫描前基线」写明允许只读子代理D-09仅回传文本、不得写文件
(4) 写 `## 发现` 表头列序固定ID / 接口名 / 所属文件:行 / 分类 / 问题描述 / 影响 / 置信 / 依据),并**实测**填入每类各一条(不得直接照抄 04-PATTERNS.md 的锚点定性,必须打开基准文件核对):
- ① 错配 `#04-01-M-01`:以 `packages-user/client-modules/src/action/move.ts:3``// @ts-expect-error 需要重构` + `import { HeroMover, IMoveController } from '@user/data-state'`)为起点,打开 `packages-user/data-state/src/types.ts`(必要时含该包 barrel逐字确认这两个符号是否仍导出、形状/签名是否已变;「依据」列必须写出基准文件的 `file:line`
- ③ 多余旧路径 `#04-01-L-01`:以 `packages-user/client-modules/src/render/ui/main.tsx:99-128` 的 legacy `core.status.*` / `core.isReplaying()` 读取为准,判定其应由 `ICoreState``packages-user/data-state/src/types.ts`)的哪个成员取代;「依据」列写出该成员的基准 `file:line`
若执行时锚点已漂移、或该读取已迁到新接口,改取同组其它锚点(同文件其它行或 04-PATTERNS.md §4/§5 的其它锚点),并在「未能从阅读确定」段记录替换原因;不得把未核实项写成缺陷。
(5) 写 `## ② 数据端缺失接口`:按 `### #04-01-G-N标题严重度` + `- **现状**` / `- **期望**` 登记**经阅读确认**的「渲染端需要而数据端未提供」项;若阅读后确认不存在此类项,则本节不登记条目,改为写「本步未确认数据端缺失接口」的说明。节末**必须**写结论行,取以下之一:`**②节结论:** 本步确认 N 项数据端缺失接口(见上)` 或 `**②节结论:** 本步未确认数据端缺失接口;相关不确定项见「未能从阅读确定(未猜测)」`。不得为满足门禁而臆造缺失项。
(6) 写 `## 触发序列(举例)`(至少 1 条:渲染端调用 → 按旧形状解读 → 结果偏离的链路)、`## 判定为「匹配」的同类边界`(至少登记 `core.material.*` 素材族为何不算数据端错配,并给出判据)、`## 未能从阅读确定(未猜测)`(本 tracer 阶段至少登记「② 节是否有缺失项」的不确定性来源)、`## 处置`(含:本步只读;后续适配实施与双布局未在本 run 规划、由用户在对账结果出来后决定拆分;双布局既有资产 `packages-user/client-modules/src/render/use.ts``Orientation` / `onOrientationChange``packages-user/client-modules/src/shared.ts` 的布局常量仅登记为后续起点,不展开)。
(7) 全程不得修改任何被查源码;本任务只写审计文档一个文件。
</action>
<verify>
<automated>node -e "const fs=require('fs');const p='.planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md';const t=fs.readFileSync(p,'utf8');const need=['## \u80cc\u666f','## \u65b9\u6cd5','## \u53d1\u73b0','## \u2461 \u6570\u636e\u7aef\u7f3a\u5931\u63a5\u53e3','## \u89e6\u53d1\u5e8f\u5217\uff08\u4e3e\u4f8b\uff09','## \u5224\u5b9a\u4e3a\u300c\u5339\u914d\u300d\u7684\u540c\u7c7b\u8fb9\u754c','## \u672a\u80fd\u4ece\u9605\u8bfb\u786e\u5b9a\uff08\u672a\u731c\u6d4b\uff09','## \u5904\u7f6e'];const miss=need.filter(h=>t.indexOf(h)<0);if(miss.length>0){console.error('MISSING HEADINGS '+miss.length);process.exit(1)}const rows=t.split(/\r?\n/).filter(l=>/^\|\s*#04-01-[ML]-\d+\s*\|/.test(l));const bad=rows.filter(l=>!/:\d+/.test(l));if(rows.length<2||bad.length>0){console.error('BAD FINDING ROWS '+rows.length+'/'+bad.length);process.exit(1)}if(t.indexOf('#04-01-M-')<0||t.indexOf('#04-01-L-')<0){console.error('MISSING CATEGORY PREFIX');process.exit(1)}if(t.indexOf('\u2461\u8282\u7ed3\u8bba')<0){console.error('GAP SECTION WITHOUT CONCLUSION');process.exit(1)}console.log('OK headings 8 findingRows '+rows.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "MISSING HEADINGS" / "BAD FINDING ROWS" / "MISSING CATEGORY PREFIX" / "GAP SECTION WITHOUT CONCLUSION"</fails_when>
</verify>
<acceptance_criteria>
- 审计文档存在于约定路径,且 8 个二级标题逐字存在(`## 背景` / `## 方法` / `## 发现` / `## ② 数据端缺失接口` / `## 触发序列(举例)` / `## 判定为「匹配」的同类边界` / `## 未能从阅读确定(未猜测)` / `## 处置`
- 发现表列序为 `ID | 接口名 | 所属文件:行 | 分类 | 问题描述 | 影响 | 置信 | 依据`,且至少含 1 条 `#04-01-M-*` 与 1 条 `#04-01-L-*`,每条都带 `file:line` 锚点
- `#04-01-M-01` 的「依据」列引用 `packages-user/data-state/src/types.ts`(或该包基准)的具体行,且其定性来自实际阅读而非锚点照抄
- 「② 数据端缺失接口」节存在且含固定结论行(`②节结论`
- `## 方法` 记录了 `git status --porcelain -- packages-user/client-base packages-user/client-modules` 的扫描前基线输出
- 被查两个渲染端包零改动
</acceptance_criteria>
<done>审计文档骨架与三分类各一条实测记录端到端贯通,结构/ID 门禁转绿;后续 Task 2/3 只需在同一模板上横向补齐,无需再改结构</done>
</task>
<task type="auto">
<name>Task 2① 错配全量清点 —— 数据端 import / 契约桥 / 组合根继承 / 类型直连逐字比对 + 触发序列</name>
<files>.planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md</files>
<read_first>
- .planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.mdTask 1 产出的现有内容,在同一模板上追加)
- .planning/phases/04-render-adaptation/04-PATTERNS.md§2 A1 契约桥、§3 A2 组合根/单例、§5 A4 新接口对照面、§6 A5 类型直连;「既有重构标记的处置口径」)
- packages-user/data-common/src/types.tsL0 `IDataCommon` 基准)
- packages-user/data-base/src/types.tsL1 `IStateBase``IGameMap` / `IMapLayer` / `IGameMapHooks` 基准)
- packages-user/data-system/src/types.tsL2 `IStateSystem` 基准)
- packages-user/data-state/src/types.tsL3 `ICoreState` 基准)
- packages-user/data-common/src/replay/types.ts 与 packages-user/data-common/src/store/types.ts录像/图块 raw-data 契约基准)
- packages-user/client-base/src/types.ts`IClientBase extends ICoreState`)与 packages-user/client-modules/src/types.ts实现层契约 extends 系统层契约)
- packages-user/client-modules/src/client.ts:46`export class ClientCore extends CoreState implements IClientCore`)与 packages-user/client-modules/src/core.ts:1-6单例 + 既有重构 TODO
- packages-user/client-modules/src/render/map/{renderer,moving,element,status,vertex,types}.ts、packages-user/client-modules/src/render/elements/props.ts、packages-user/client-modules/src/render/map/extension/hero.ts数据端类型/契约直连与 `@ts-expect-error 需要重构` 标记)
</read_first>
<action>
(1) 枚举被查两包内所有 `@user/data-common|data-base|data-system|data-state` 的 import`import type`),逐处打开基准文件比对**接口名、成员、参数/返回签名**是否仍成立A1 契约桥(`IClientBase extends ICoreState` 后 `ICoreState` 新增/改名/删成员会使桥断裂、A2 组合根(`ClientCore extends CoreState` 的 override 签名、`loading.once('coreInit')` 生命周期、`core.firstData.name` 读取点、A5 类型直连(`IGameMap` / `IMapLayer` / `IGameMapHooks` / `ISaveableContent` / `IHeroMoveController` 等)。
(2) 逐条登记为 `#04-01-M-N`N 从 2 递增,`M-01` 已在 Task 1 占用):填入 接口名 / 所属文件:行 / 分类=`① 错配` / 问题描述(旧形状或旧签名具体差在哪)/ 影响 / 置信(高/中/低)/ 依据(基准文件 `file:line`)。**一条记录只归一类**;跨类则拆条。
(3) 带 `// @ts-expect-error 需要重构` 的 import`action/move.ts:3`、`render/map/extension/hero.ts:5,7,9`、`render/ui/main.tsx:28`)是强线索但**不是结论**:必须与基准文件实签核对后才定性;核对后仍成立的保持「匹配」并写入边界段,核对后发现签名已变的登记为 `#04-01-M-*`
(4) 把最高置信的 13 条错配的「调用 → 按旧形状解读 → 结果偏离」链路写进 `## 触发序列(举例)`(格式对照 07-LOADSTATE-AUDIT.md:29-35
(5) 若某 import 的定性无法从静态阅读确定(例如依赖运行时形状、或基准文件正在被用户并行改动),写入 `## 未能从阅读确定(未猜测)`**不得**登记为错配。
(6) 允许派只读子代理分派 A1/A2/A4/A5 扫描组D-09子代理只回传文本**不得写文件**;主执行者负责汇总与定性一致性。
(7) 只修改审计文档;不得改动任何生产源码,不得新增计划文件。
</action>
<verify>
<automated>node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md','utf8');const rows=t.split(/\r?\n/).filter(l=>/^\|\s*#04-01-M-\d+\s*\|/.test(l));if(rows.length===0){console.error('NO MISMATCH ROWS');process.exit(1)}const noAnchor=rows.filter(l=>!/:\d+/.test(l));const noBase=rows.filter(l=>!/data-(common|base|system|state)\/src\//.test(l));if(noAnchor.length>0){console.error('MISMATCH ROW WITHOUT ANCHOR '+noAnchor.length);process.exit(1)}if(noBase.length>0){console.error('MISMATCH ROW WITHOUT BASELINE '+noBase.length);process.exit(1)}console.log('OK mismatch rows '+rows.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "NO MISMATCH ROWS" / "MISMATCH ROW WITHOUT ANCHOR" / "MISMATCH ROW WITHOUT BASELINE"</fails_when>
</verify>
<acceptance_criteria>
- 每条 `#04-01-M-*` 记录都含 `file:line` 锚点(指向 `packages-user/client-base``packages-user/client-modules`
- 每条 `#04-01-M-*` 的「依据」列引用至少一个 `data-common|data-base|data-system|data-state``src/*.ts` 基准文件
- A1 契约桥、A2 组合根、A5 类型直连三组均有实际比对结论登记为错配或写明为匹配边界A4 新接口调用作为「匹配」判据进入边界段
- `## 触发序列(举例)` 至少含 1 条错配链路,且与发现表中的 `#04-01-M-*` 对应
- 无法定论的 import 已进入「未能从阅读确定(未猜测)」,未被写成错配
- 被查两个渲染端包零改动;未新增任何计划文件
</acceptance_criteria>
<done>① 错配在 A1/A2/A4/A5 四组上全量清点完毕,每条可追溯到接口名与基准 `file:line`;触发序列与边界段同步更新</done>
</task>
<task type="auto">
<name>Task 3③ 多余旧路径全量清点 + ② 缺失节收口 + 匹配边界/未确定/处置定稿 + 只读与范围门禁</name>
<files>.planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md</files>
<read_first>
- .planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.mdTask 1/2 产出,做定稿而非重写)
- .planning/phases/04-render-adaptation/04-PATTERNS.md§4 A3 legacy `core.*` 读取锚点表与判定口径、§7 A6 双布局既有资产、Shared Patterns「分类口径」「证据纪律」「只读约束」
- .planning/phases/07-data-fixes/07-LOADSTATE-AUDIT.md:37-46「判定为安全/匹配的同类边界」写法)与 :48-53「未能从阅读确定未猜测」写法
- packages-user/data-base/src/game.ts:155,166,266-267`hook`、`GameListener`、`@deprecated gameListener`
- packages-user/data-state/src/core.ts顶层装配与 legacy bridge 落点、packages-user/data-state/src/ins.ts`state` 单例)
- packages-user/entry-data/src/mota.ts:125-136`r()` / `rf()` 渲染调用门控)
- packages-user/data-system/src/path/types.ts 与 packages-user/data-state/src/types.ts判定 legacy 读取的替代接口目标)
- packages-user/client-modules/src/action/move.ts:66-67,97,123,156、packages-user/client-modules/src/render/ui/{main,statistics,viewmap,settings,save,toolbar,statusBar}.tsx、packages-user/client-modules/src/render/components/choices.tsx、packages-user/client-modules/src/fallback/load.ts、packages-user/client-modules/src/render/utils/saves.tsA3 锚点面)
- packages-user/client-base/src/load/loader.ts:103,129,151,167,188,284,418 与 packages-user/client-modules/src/render/elements/{cache,misc}.ts、packages-user/client-modules/src/render/components/misc.tsx渲染侧素材族用于「匹配」判据
- packages-user/client-modules/src/render/use.ts:22-52 与 packages-user/client-modules/src/shared.ts:33-97双布局既有资产
</read_first>
<action>
(1) 按 04-PATTERNS.md §4 的 A3 锚点表逐处核对 legacy `core.*` 读取,对每处先判定「读的是数据端状态」还是「渲染侧素材/全局」:
- 数据端状态类(如 `core.isReplaying()` / `core.isPlaying()` / `core.status.*` / `core.getNextLvUpNeed()` / `core.itemCount()` / `core.getLvName()` / `core.maps.*` / `core.flags.*` / `core.firstData.*` / `core.startGame()` / `core.status.replay.*`)→ 登记为 `#04-01-L-N`N 从 2 递增,`L-01` 已在 Task 1 占用),分类=`③ 多余旧路径`,并在「依据」列写出应取代它的新接口/基准 `file:line`
- 纯渲染侧素材/全局(`core.material.images.*`、`core.materials`、`core.icons.icons` 等)→ **不登记为缺陷**,写入 `## 判定为「匹配」的同类边界` 并给出判据(对照 04-PATTERNS.md 「不得因出现 `core.` 就一律登记」)。
每条记录含 接口名 / 所属文件:行 / 分类 / 问题描述 / 影响 / 置信 / 依据。
(2) 若同一处 legacy 读取同时是「形状已变」而非单纯旧路径,按「一条只归一类」拆条(错配归 `#04-01-M-*`,旧路径归 `#04-01-L-*`),并在两条之间互相引用 ID。
(3) `## ② 数据端缺失接口` 定稿:逐条确认/补全 `### #04-01-G-N` 的「现状 / 期望」;最终结论行取固定两式之一。凡不能确认的候选一律不登记为缺失项,转入「未能从阅读确定(未猜测)」。
(4) 定稿 `## 判定为「匹配」的同类边界`(至少覆盖 `core.material.*` 素材族、`hook`/`loading` 订阅新接口用法),定稿 `## 未能从阅读确定(未猜测)`(逐条说明为何静态阅读不能定论),定稿 `## 处置`(本步只读、零生产改动;后续适配与双布局未在本 run 规划、由用户决定拆分;`render/use.ts` 的 `Orientation`/`onOrientationChange` 与 `shared.ts` 布局常量登记为后续双布局起点,不展开实施)。
(5) 全表 ID 唯一性自检:`#04-01-M-*` / `#04-01-L-*` / `#04-01-G-*` 各自序号连续且不重复;每条分类列与 ID 前缀一致M=① 错配、L=③ 多余旧路径、G=② 数据端缺失)。
(6) 门禁与提交:运行只读门禁与范围门禁(见 `<verify>`);随后以 `docs(04-01): 渲染端 ↔ 数据端接口对账只读清点D-01 第一步)` 提交**仅**审计文档,不得提交任何被查源码改动,不得新增后续步骤的计划文件。
</action>
<verify>
<automated>node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md','utf8');const rows=t.split(/\r?\n/).filter(l=>/^\|\s*#04-01-L-\d+\s*\|/.test(l));if(rows.length===0){console.error('NO LEGACY ROWS');process.exit(1)}const bad=rows.filter(l=>!/:\d+/.test(l));if(bad.length>0){console.error('LEGACY ROW WITHOUT ANCHOR '+bad.length);process.exit(1)}const lines=t.split(/\r?\n/);const gh=lines.map((l,i)=>({l:l.trim(),i:i})).filter(o=>/^### #04-01-G-\d+/.test(o.l));for(const g of gh){const body=lines.slice(g.i+1,g.i+7).join('\n');if(body.indexOf('\u73b0\u72b6')<0||body.indexOf('\u671f\u671b')<0){console.error('GAP ENTRY MISSING FIELDS');process.exit(1)}}if(t.indexOf('\u2461\u8282\u7ed3\u8bba')<0){console.error('GAP SECTION WITHOUT CONCLUSION');process.exit(1)}console.log('OK legacy rows '+rows.length+' gaps '+gh.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "NO LEGACY ROWS" / "LEGACY ROW WITHOUT ANCHOR" / "GAP ENTRY MISSING FIELDS" / "GAP SECTION WITHOUT CONCLUSION"</fails_when>
<automated>git status --porcelain -- packages-user/client-base packages-user/client-modules</automated>
<fails_when>stdout 非空(任一被查渲染端包出现改动/新增行)—— 违反只读约束</fails_when>
<automated>node -e "const cp=require('child_process');const out=cp.execSync('git status --porcelain -- .planning/phases/04-render-adaptation',{encoding:'utf8'});const extra=out.split(/\r?\n/).filter(l=>/\d{2}-\d{2}-PLAN\.md/.test(l)&&l.indexOf('04-01-PLAN.md')<0);if(extra.length>0){console.error('UNEXPECTED PLAN FILES '+extra.length);process.exit(1)}console.log('OK plan scope')"</automated>
<fails_when>non-zero exit, 或 stderr 含 "UNEXPECTED PLAN FILES"(出现了 04-01 以外的计划文件)</fails_when>
</verify>
<acceptance_criteria>
- A3 锚点表的每一处 `core.*` 读取都已判定并落位:数据端状态类登记为 `#04-01-L-*`、素材类进入「判定为『匹配』的同类边界」,无遗漏、无误报
- 每条 `#04-01-L-*``file:line` 锚点,并在「依据」列指出应取代它的新接口/基准位置
- 「② 数据端缺失接口」节定稿:条目(如有)均含「现状 / 期望」,且节末结论行取固定两式之一;未确认候选在「未能从阅读确定(未猜测)」中逐条列出
- `## 判定为「匹配」的同类边界` / `## 未能从阅读确定(未猜测)` / `## 处置` 三段均非空且符合 07-LOADSTATE-AUDIT 的写法;`## 处置` 含双布局资产登记与「后续步骤未在本 run 规划」的明确表述
- ID 唯一且前缀与分类列一一对应M=① 错配、L=③ 多余旧路径、G=② 数据端缺失)
- `git status --porcelain -- packages-user/client-base packages-user/client-modules` 为空;`.planning/phases/04-render-adaptation` 下无 04-01 以外的计划文件
- 审计文档已以 `docs(04-01)` 提交,提交内容仅含该文档
</acceptance_criteria>
<done>三类记录全部落位且互斥、匹配边界与未确定段定稿、缺失节收口、只读与范围门禁全绿、审计文档已提交;用户可据此决定阶段 4 剩余工作的拆分</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| 数据端接口基准 → 审计文档 | 只读引用 `data-*``types.ts` / `core.ts` / `ins.ts`;基准不变,仅记录差异 |
| 渲染端源码 → 审计文档 | 只读引用被查两包源码;不得产生任何写回 |
| 执行者 → 仓库工作树 | 本 run 唯一允许写入的文件是审计文档;被查渲染端包必须保持零改动 |
| 审计文档 → 后续适配计划 | 「处置」段的结论被下游用户/计划消费,错报会直接污染阶段 4 的拆分决策 |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|-----------|----------|-----------|----------|-------------|-----------------|
| T-04-01 | Tampering | 被查渲染端包 `packages-user/client-base` / `client-modules` | high | mitigate | Task 13 均为只读Task 3 门禁 `git status --porcelain -- packages-user/client-base packages-user/client-modules` 必须为空,非空即失败 |
| T-04-02 | Repudiation | 发现表记录(无锚点则不可追溯) | medium | mitigate | 每条记录强制含 `file:line``#04-01-M-*` 强制在「依据」列引用数据端基准文件;由 Task 2/3 的 `<verify>` 门禁机器判定 |
| T-04-03 | Spoofing | 「判定为『匹配』的同类边界」段 | medium | mitigate | 该段必须给出判据(`core.material.*` 素材族 vs 数据端状态),且「未能从阅读确定」段为强制段——不得把未核实项伪装成匹配或缺陷 |
| T-04-04 | Elevation of Privilege | 被查范围(`packages/**`、`entry-client`、`legacy-plugin-*`、`data-fallback`、`data-*` 本体) | medium | mitigate | 范围由 D-03/D-04 限定;范围外路径不登记为被查项,只能作为理解接线方式的参考 |
| T-04-05 | Information Disclosure | 审计文档引用的源码行与接口名 | low | accept | 记录只含接口名、文件行号、问题描述与影响,不含凭据/密钥;如需提及敏感常量仅以标识符呈现 |
| T-04-06 | Denial of Service | 只读子代理越权写入D-09 | low | mitigate | Task 2/3 明确子代理仅回传文本、不得写文件;最终由主执行者的只读门禁统一验证 |
| T-04-SC | Tampering | npm / pnpm 依赖安装 | high | mitigate | 本计划零依赖变更、不修改任何 `package.json`出现安装需求即暂停并要求用户确认包合法性blocking human checkpoint不得自动继续 |
</threat_model>
<verification>
1. 结构门禁Task 1审计文档 8 个二级标题逐字存在,`#04-01-M-*` / `#04-01-L-*` 各至少 1 条且每条含 `file:line`,「② 数据端缺失接口」节含固定结论行。
2. 错配门禁Task 2每条 `#04-01-M-*``file:line` 且「依据」列引用 `data-common|data-base|data-system|data-state``src/*.ts`
3. 旧路径与缺失节门禁Task 3每条 `#04-01-L-*``file:line`;每个 `### #04-01-G-*` 之后紧跟的 6 行内含「现状」「期望」;② 节结论行存在。
4. 只读门禁Task 3`git status --porcelain -- packages-user/client-base packages-user/client-modules` 输出为空。
5. 范围门禁Task 3`.planning/phases/04-render-adaptation` 下除 `04-01-PLAN.md` 外无其它 `*-PLAN.md`(后续步骤未被规划)。
6. 人工核对:`## 判定为「匹配」的同类边界` 的素材族判据、`## 未能从阅读确定(未猜测)` 的逐条理由、`## 处置` 的双布局资产登记与「后续步骤未在本 run 规划」表述均齐备。
7. 提交:`docs(04-01)` 仅含审计文档。
</verification>
<success_criteria>
- `.planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md` 按 8 段骨架产出,三类记录(① 错配 `#04-01-M-*` / ② 数据端缺失 `#04-01-G-*`(独立节)/ ③ 多余旧路径 `#04-01-L-*`)互斥且完整,每条精确到接口名 + `file:line` + 描述 + 影响D-06/D-07/D-08
- 对账以数据端接口签名为基准D-04/D-05每条错配可追溯到基准文件行未受用户并行修改实现的影响。
- 已确认匹配的读取(`core.material.*` 素材族、`hook`/`loading` 订阅)进入边界段而非误报;无法定论的链路进入「未能从阅读确定(未猜测)」而非写成缺陷。
- 被查两个渲染端包零改动;本 run 只新增审计文档一个文件未规划、未实施后续适配与双布局D-01/D-02/D-03
- 双布局既有资产已在「处置」段登记为后续起点REND-02 的已知落点且明确「验收边界待用户补齐」REND-01/REND-02 的 specless fallback flagged assumptions
</success_criteria>
<output>
Create `.planning/phases/04-render-adaptation/04-01-SUMMARY.md` when done
</output>