fix(04): revise 04-02 plan based on checker feedback

This commit is contained in:
unanmed 2026-09-18 11:33:03 +08:00
parent 4a1e9b36f8
commit e326f4b9c8

View File

@ -2,7 +2,7 @@
phase: 04-render-adaptation
plan: 2
type: execute
wave: 1
wave: 2
depends_on:
- 04-01
files_modified:
@ -18,12 +18,12 @@ estimate:
confidence: low
must_haves:
truths:
- "`.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md` 存在,含 7 个二级标题:`## 背景` / `## 数据端现行接口` / `## 渲染端现状` / `## 逐成员对账(匹配 / 渲染端需改 / 数据端缺失)` / `## 缺失接口候选` / `## 未能从阅读确定(未猜测)` / `## 处置`;全文为 CRLF 换行(dev.md「换行使用 CRLF 格式」)"
- "`.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md` 存在,含 8 个二级标题:`## 背景` / `## 方法` / `## 数据端现行接口` / `## 渲染端现状` / `## 逐成员对账(匹配 / 渲染端需改 / 数据端缺失)` / `## 缺失接口候选` / `## 未能从阅读确定(未猜测)` / `## 处置`;`## 方法` 含「扫描前基线」条目(原样记录起始 `git status --porcelain` 输出);全文为 CRLF 换行(dev.md「换行使用 CRLF 格式」)"
- "逐成员对账表每条 `#04-02-R-N` 精确到接口/成员名,同行同时含渲染端 `client-base|client-modules` 与数据端 `data-common|data-base|data-state` 两侧 `file:line` 锚点(数据端无对位时写「无对位,最接近:<file:line>」),结论取 匹配 / 渲染端需改 / 数据端缺失 三态之一"
- "带 `// @ts-expect-error 需要重构` 的 import 一律先归「渲染端需改」(用户规则,见 04-RENDER-INTERFACE-AUDIT.md 处置口径),不归「数据端缺失」"
- "「缺失接口候选」独立成节,每条 `### #04-02-G-N:标题` 含 `**渲染端需求**` / `**数据端现状**` / `**待确认**` 三段,数据端现状引用最接近的现行成员 `file:line`;候选只记录、不设计、不修复(D-13),未经用户裁决不视为最终缺失清单"
- "`HeroRendering` 相关(alpha / 贴图 / 渲染粒度钩子)与 legacy `core.*` 为显式排除项(D-18 / D-12):文中不出现针对它们的对账行或缺失候选,仅在「处置」与范围排除说明中登记排除理由"
- "全部生产代码零改动:`git status --porcelain -- packages-user/client-base packages-user/client-modules` 为空;本次只新增交付文档与 `04-02-PLAN.md`,不新增任何适配计划(04-03+ 未规划)"
- "`HeroRendering` **渲染状态**相关成员(`IHeroRendering`、`hero.image` / 贴图别名、`onSetImage` / `onSetAlpha` / `onSetFollowerAlpha`)与 legacy `core.*` 为显式排除项(D-18 / D-12):文中不出现针对它们的对账行或缺失候选,仅在「处置」与范围排除说明中登记排除理由。边界另一侧——勇士移动契约与移动钩子(`IObjectMover` / `IObjectMoverHooks` / `IMoverController` / `IHeroMover` / `IHeroLocationHooks` / `IHeroFollowersControllerHooks` / `IHeroStateHooks`,以及 `onMoveHero` / `onJumpHero` / `onTurnHero` / `onStartMove` / `onEndMove` 与 step 钩子)——**仍在范围内**,照常登记对账与候选"
- "生产代码相对规划时基线零**新增**改动:`git status --porcelain -- packages-user/client-base packages-user/client-modules` 只含规划时既有的、与勇士移动无关的用户未提交改动(`packages-user/client-base/src/load/**`,D-17 范围外,**不得回滚 / 暂存 / 提交**),不出现任何新增条目;本次只新增交付文档与 `04-02-PLAN.md`,不新增任何适配计划(04-03+ 未规划)"
- "无法从静态阅读定论的链路进入「未能从阅读确定(未猜测)」而非写成缺失或错配;文档引用的生产路径均为 tracked 源码路径(可被 `git ls-files` 命中)"
artifacts:
- path: .planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md
@ -38,11 +38,12 @@ must_haves:
- "**FLAGGED ASSUMPTION(specless fallback)— REND-01 edge unresolved:** 未提供 REND-01(渲染端适配新数据层接口)的验收边界;本计划**不发明**阈值,只交付勇士移动子系统的只读接口探索账本,供其适配阶段消费。用户须在规划适配工作前补齐边界判据。"
- "**FLAGGED ASSUMPTION(specless fallback)— REND-02 edge unresolved:** 未提供 REND-02(移动端与桌面端双布局)的验收边界;本计划不涉及双布局实现,也不发明其验收阈值。"
- "数据端接口签名在规划后可能仍被用户并行修改**实现**但不改签名(D-05);本计划以执行当日基准文件的现行签名为准,若发现签名已变,以当日文件为准并在「未能从阅读确定(未猜测)」记录该变化。"
- "数据端包(`data-common` / `data-base` / `data-state`)工作树可能因用户并发重构而非空;只读门禁只对渲染端两包(`client-base` / `client-modules`)与计划文件范围做机器断言,不对数据端工作树状态做断言(否则会与用户的进行中工作冲突)。"
- "数据端包(`data-common` / `data-base` / `data-state`)工作树可能因用户并发重构而非空;只读门禁不对数据端工作树状态做机器断言(否则会与用户的进行中工作冲突)。"
- "被查渲染端两包(`client-base` / `client-modules`)在规划时已带**与勇士移动无关**的用户未提交改动:` M packages-user/client-base/src/load/loader.ts`、` M packages-user/client-base/src/load/types.ts`(`git status --porcelain` 基线两条)。本计划**不回滚、不暂存、不提交**它们;只读门禁以「相对该基线无**新增**条目」为准(仅允许 `packages-user/client-base/src/load/` 下条目,其余任何条目即失败)。"
prohibitions:
- "不得修改或新建任何生产代码(`packages-user/client-base`、`packages-user/client-modules`、`packages-user/data-common`、`packages-user/data-base`、`packages-user/data-state`)——本步是只读接口探索(D-17)。"
- "不得规划、设计或实施勇士移动适配工作(04-03+ 适配计划不在本 run)。"
- "不得把 `HeroRendering` 相关(alpha / 勇士贴图 image / 渲染粒度钩子)纳入对账行或缺失候选(D-18)。"
- "不得把 `HeroRendering` **渲染状态**相关成员(`IHeroRendering`、`hero.image` / 勇士贴图别名、`onSetImage` / `onSetAlpha` / `onSetFollowerAlpha`)纳入对账行或缺失候选(D-18)——它们属 D-10 待删除的数据端渲染状态子系统。勇士移动契约与移动钩子(`IObjectMover` / `IHeroMover` / `IHeroLocationHooks` / `IHeroFollowersControllerHooks` / `IHeroStateHooks`,`onMoveHero` / `onJumpHero` / `onTurnHero` / `onStartMove` / `onEndMove` 等)**在范围内,不适用本条**。"
- "不得把 legacy `core.*`(含 `client.*` 以外的旧全局)纳入对账行或缺失候选(D-12,属第五阶段)。"
- "不得为缺失接口候选设计解决方案,也不得新增或改写数据端接口(D-13)。"
- "不得写入缺少两侧 `file:line` 锚点的对账行;静态阅读无法定论的链路只能进「未能从阅读确定(未猜测)」。"
@ -56,7 +57,7 @@ Phase 4 第二个增量(D-17):对**勇士移动子系统**做**只读接
对账结论固定三态:**匹配 / 渲染端需改 / 数据端缺失**。带 `// @ts-expect-error 需要重构` 的 import 一律先归「渲染端需改」(不归数据端缺失)。
**显式排除:** `HeroRendering` 相关(alpha / 贴图 / 渲染粒度钩子)按 D-18 排除(用户先改数据端,之后再处理);legacy `core.*` 按 D-12 排除(属第五阶段)。二者不产生任何对账行或候选。
**显式排除:** `HeroRendering` **渲染状态**相关成员(`IHeroRendering`、`hero.image` / 贴图别名、`onSetImage` / `onSetAlpha` / `onSetFollowerAlpha`)按 D-18 排除(用户先改数据端,之后再处理);legacy `core.*` 按 D-12 排除(属第五阶段)。二者不产生任何对账行或候选。**边界另一侧**——勇士移动契约与移动钩子(`IObjectMover` / `IObjectMoverHooks` / `IMoverController` / `IHeroMover` / `IHeroLocationHooks` / `IHeroFollowersControllerHooks` / `IHeroStateHooks`,`onMoveHero` / `onJumpHero` / `onTurnHero` / `onStartMove` / `onEndMove` 与 step 钩子)——**不属排除范围**,照常登记。
**后续适配(04-03+)在本 run 不规划**:适配依赖本探索输出,且需用户先裁决缺失候选(D-13/D-17)。
@ -67,8 +68,8 @@ Output: `04-HERO-MOVER-INTERFACE.md`(本 run 唯一新增交付物;零生产
**Artifacts this phase produces(本计划产出物)**:
- **文件**:`.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md`(新建,唯一写入目标)。
- **章节**(7 个二级标题,下游验证按标题名核对):`## 背景` / `## 数据端现行接口` / `## 渲染端现状` / `## 逐成员对账(匹配 / 渲染端需改 / 数据端缺失)` / `## 缺失接口候选` / `## 未能从阅读确定(未猜测)` / `## 处置`。
- **元信息块**(标题下 5 行,对照 04-RENDER-INTERFACE-AUDIT.md:3-6):`**日期:**` / `**范围:**`(勇士移动子系统;排除 HeroRendering 与 core.*) / `**性质:**`(只读接口探索,未修改任何生产代码) / `**接口基准:**`(`data-common/src/common/mover.ts`、`data-base/src/hero/{types,mover}.ts` 及 barrel 现行导出) / `**输入账本:**`(`04-RENDER-INTERFACE-AUDIT.md` 的 `#04-01-M-01..05`)。
- **章节**(8 个二级标题,下游验证按标题名核对):`## 背景` / `## 方法` / `## 数据端现行接口` / `## 渲染端现状` / `## 逐成员对账(匹配 / 渲染端需改 / 数据端缺失)` / `## 缺失接口候选` / `## 未能从阅读确定(未猜测)` / `## 处置`。
- **元信息块**(标题下 5 行,对照 04-RENDER-INTERFACE-AUDIT.md:3-6):`**日期:**` / `**范围:**`(勇士移动子系统;排除 `HeroRendering` 渲染状态(`IHeroRendering` / 贴图别名 / alpha 钩子)与 `core.*`) / `**性质:**`(只读接口探索,未修改任何生产代码;相对规划时基线零新增改动) / `**接口基准:**`(`data-common/src/common/mover.ts`、`data-base/src/hero/{types,mover}.ts` 及 barrel 现行导出) / `**输入账本:**`(`04-RENDER-INTERFACE-AUDIT.md` 的 `#04-01-M-01..05`)。
- **逐成员对账表列**(固定顺序):`| ID | 成员 | 渲染端锚点 | 数据端锚点 | 对账结论 | 说明 |`。
- **记录 ID 前缀**(下游按前缀检索):`#04-02-R-N` —— 逐成员对账行(结论 ∈ 匹配 / 渲染端需改 / 数据端缺失);`#04-02-G-N` —— 缺失接口候选(独立节,`### #04-02-G-N:标题` + `**渲染端需求**` / `**数据端现状**` / `**待确认**`)。
- **缺失候选节结论行**(固定写法,必须出现):`**候选结论:** 本步登记 N 项缺失接口候选(未经用户裁决,不视为最终缺失清单,见 D-13)`。
@ -87,7 +88,7 @@ Output: `04-HERO-MOVER-INTERFACE.md`(本 run 唯一新增交付物;零生产
| CONTEXT | D-12 | `core.*` 属旧引擎适配,归第五阶段 | 04-02 | COVERED | `core.*` 为显式排除项,不产生对账行/候选 |
| CONTEXT | D-13 | 缺失接口待改时逐步确认,不一次定死 | 04-02 | COVERED | 「缺失接口候选」节只登记候选 + 数据端现状,不设计、不修复、不视为最终清单 |
| CONTEXT | D-17 | 下一步=勇士移动适配;先接口探索再适配 | 04-02 | COVERED | 本计划即「先接口探索」的一半;适配半未规划 |
| CONTEXT | D-18 | `HeroRendering` 暂不处理,用户先改数据端 | 04-02 | COVERED | `HeroRendering`(alpha/贴图/渲染粒度钩子)为显式排除项 |
| CONTEXT | D-18 | `HeroRendering` 暂不处理,用户先改数据端 | 04-02 | COVERED | `HeroRendering` **渲染状态**(`IHeroRendering` / 贴图别名 / `onSetImage` / `onSetAlpha` / `onSetFollowerAlpha`)为显式排除项;勇士移动契约与移动钩子在范围内照常登记 |
| RESEARCH | — | (本阶段无 RESEARCH.md) | — | N/A | Phase 4 未产出 RESEARCH.md,无研究项需覆盖 |
## Estimate Basis(估算依据)
@ -119,14 +120,14 @@ Output: `04-HERO-MOVER-INTERFACE.md`(本 run 唯一新增交付物;零生产
<tasks>
<task type="tracer">
<name>Task 1(tracer):交付文档骨架 + 勇士移动一条端到端对账切片 —— 7 段结构 + `#04-02-R-01` 双锚点实测 + 结构/CRLF 门禁转绿</name>
<name>Task 1(tracer):交付文档骨架 + 勇士移动一条端到端对账切片 —— 8 段结构(含 `## 方法`)+ `#04-02-R-01` 双锚点实测 + 结构/CRLF 门禁转绿</name>
<files>.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md</files>
<read_first>
- .planning/phases/07-data-fixes/07-LOADSTATE-AUDIT.md:1-14(元信息块 + 背景/方法骨架)
- .planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md:1-10(「接口事实源」声明块写法)
- .planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md:1-23(元信息块/方法段写法)与 :30-34(`#04-01-M-01..05` 勇士移动既有登记,作为本计划的输入账本)
- .planning/phases/04-render-adaptation/04-CONTEXT.md(D-11 / D-12 / D-13 / D-17 / D-18)
- packages-user/client-modules/src/render/map/extension/hero.ts:76-132(构造器、`hero.addHook`、`hero.x`/`hero.y`/`hero.direction`/`hero.image` 使用点)与 :452-517(`MapHeroHook` 全部钩子方法名)
- packages-user/client-modules/src/render/map/extension/hero.ts:76-132(构造器、`hero.addHook`、`hero.x`/`hero.y`/`hero.direction` 使用点;`hero.image` 仅作 D-18 排除边界的识别点,不登记对账行或候选)与 :452-517(`MapHeroHook` 钩子方法名;贴图/alpha 钩子仅作排除边界识别)
- packages-user/client-modules/src/render/map/extension/types.ts:13-40,63-159(`IMapExtensionManager` / `IMapHeroRenderer` 契约)
- packages-user/client-modules/src/action/move.ts:1-40(`// @ts-expect-error 需要重构` 与 `HeroMover` / `IMoveController` import)
- packages-user/data-common/src/common/mover.ts:40-64,100-164,166-233,235-351(`ObjectAnimDirection` / `IMoverController` / `IObjectMoverHooks` / `IObjectMover`)
@ -135,30 +136,32 @@ Output: `04-HERO-MOVER-INTERFACE.md`(本 run 唯一新增交付物;零生产
- dev.md(「双端分离」「命名规则」「注释规范」;交付文档换行 CRLF)
</read_first>
<action>
(1) 新建 `04-HERO-MOVER-INTERFACE.md`,先写标题与 5 行元信息块:`**日期:** 2026-09-18`;`**范围:**` 勇士移动子系统(渲染端使用点 → 数据端现行接口),排除 `HeroRendering` 与 `core.*`(D-18/D-12);`**性质:**` 只读接口探索,未修改任何生产代码(D-17);`**接口基准:**` `packages-user/data-common/src/common/mover.ts`、`packages-user/data-base/src/hero/{types,mover}.ts` 及各自 barrel 现行导出(D-05);`**输入账本:**` `04-RENDER-INTERFACE-AUDIT.md` 的 `#04-01-M-01..05`。**全文使用 CRLF 换行**(dev.md「换行使用 CRLF 格式」);Task 2/3 追加内容同样保持 CRLF,不得出现孤立 `LF`。
(2) 写 `## 背景`:D-17(对账暴露的主要失配点在勇士移动;下一步先接口探索并找出缺失接口,再适配)、本步只读、明确本步不实施适配、不裁决缺失接口(D-13)、不涉 `HeroRendering`(D-18)与 `core.*`(D-12)。
(3) 写 `## 方法`:枚举两个面 ——(a)**数据端现行接口面** = `data-common` 移动器族(`IObjectMovable` / `IObjectMover` / `IObjectMoverHooks` / `IMoverController` / `ObjectMoveStep` 各族 / `ObjectAnimDirection`)+ `data-base/hero` 位置移动族(`IHeroState` / `IHeroLocation` / `IHeroMover` / `IHeroLocationHooks` / `IHeroFollowersControllerHooks` / `IHeroStateHooks` / `HeroMover`)+ 各 `index.ts` barrel 实际导出;(b)**渲染端勇士移动使用面** = `render/map/extension/{hero,types,manager}.ts` + `action/move.ts` + `client.ts` 接线点,`render/map/{moving,status,renderer}.ts` 仅作移动器契约参考(不登记为独立对账对象)。判定三态固定为 **匹配 / 渲染端需改 / 数据端缺失**;带 `// @ts-expect-error 需要重构` 的 import 一律先归「渲染端需改」,不归「数据端缺失」。证据纪律:每条含 `接口/成员名 + 两侧 file:line`;静态阅读不能定论者进「未能从阅读确定(未猜测)」,不写成缺失或错配。允许按 D-09 派只读子代理(仅回传文本、不得写文件)。
(4) 写 7 个二级标题占位:`## 背景` / `## 数据端现行接口` / `## 渲染端现状` / `## 逐成员对账(匹配 / 渲染端需改 / 数据端缺失)` / `## 缺失接口候选` / `## 未能从阅读确定(未猜测)` / `## 处置`(Task 2/3 在同一骨架上横向补齐,不再改结构)。
(5) **端到端切片(本任务唯一实测对账行)**:以 `MapHeroRenderer` 构造期的钩子注册为对象,从渲染端 `packages-user/client-modules/src/render/map/extension/hero.ts:81`(`hero.addHook(new MapHeroHook(this))`)与 :457-516(`MapHeroHook implements Partial<IHeroMoveControllerHooks>`:`onSetImage` / `onSetPosition` / `onTurnHero` / `onStartMove` / `onMoveHero` / `onEndMove` / `onJumpHero` / `onSetAlpha` / `onAddFollower` / `onRemoveFollower` / `onRemoveAllFollowers` / `onSetFollowerAlpha`)出发,打开数据端基准核对:`addHook` 来自 `IHookable`(经 `packages-user/data-common/src/common/mover.ts:235` 的 `IObjectMover<T> extends IHookable<...>` 承接),`onSetPos` 在 `IHeroLocationHooks`(`packages-user/data-base/src/hero/types.ts:283`)与 `IObjectMoverHooks`(`packages-user/data-common/src/common/mover.ts:216`)各有对位,而 `onSetPosition`/`onStartMove`/`onEndMove`/`onMoveHero`/`onJumpHero`/`onTurnHero`/`onAddFollower`/`onRemoveFollower`/`onRemoveAllFollowers`/`onSetFollowerAlpha` 在数据端无同名对位。据此在 `## 逐成员对账(...)` 下写表头 `| ID | 成员 | 渲染端锚点 | 数据端锚点 | 对账结论 | 说明 |` 与 `#04-02-R-01` 一行:成员=`IHeroMoveControllerHooks` 钩子族;渲染端锚点=hero.ts:81,457-516;数据端锚点=`data-common/src/common/mover.ts:166,216` 与 `data-base/src/hero/types.ts:277,283`(逐项写「有对位」或「无对位,最接近:<file:line>」);结论=`渲染端需改`(随 `IHeroMoveControllerHooks` 整体重构)。同时在 `## 数据端现行接口` 与 `## 渲染端现状` 各写至少 1 条带 `file:line` 的最小条目,证明三段相互可达。
(6) 写 `## 处置` 初稿:本步只读、未改任何生产代码;适配工作(04-03+)不在本 run 规划;明确 `HeroRendering`(D-18)与 `core.*`(D-12)为排除项;缺失接口候选须经用户逐步裁决(D-13);`getHeroStatusOn` 类裸函数(D-11)不属本增量范围。
(7) 全程不得修改任何被查源码;本任务只写该交付文档一个文件。
(0) **起始基线快照(只读前置)**:先执行 `git status --porcelain -- packages-user/client-base packages-user/client-modules`,把输出原样记为「扫描前基线」并写入 `## 方法` 的一条 bullet(写法对照 04-RENDER-INTERFACE-AUDIT.md:21)。规划时该输出为两条与勇士移动无关的用户未提交改动:` M packages-user/client-base/src/load/loader.ts`、` M packages-user/client-base/src/load/types.ts`。**不得回滚、暂存、提交或修改这些既有改动**;若基线中出现 `packages-user/client-base/src/load/` 之外的条目,停下向用户报告,不得继续(后续 Task 3 的只读门禁以该基线为基准,只断言无**新增**条目)。
(1) 新建 `04-HERO-MOVER-INTERFACE.md`,先写标题与 5 行元信息块:`**日期:** 2026-09-18`;`**范围:**` 勇士移动子系统(渲染端使用点 → 数据端现行接口),排除 `HeroRendering` 渲染状态(`IHeroRendering` / 贴图别名 / alpha 钩子,D-18)与 `core.*`(D-12);`**性质:**` 只读接口探索,未修改任何生产代码,相对规划时基线零新增改动(D-17);`**接口基准:**` `packages-user/data-common/src/common/mover.ts`、`packages-user/data-base/src/hero/{types,mover}.ts` 及各自 barrel 现行导出(D-05);`**输入账本:**` `04-RENDER-INTERFACE-AUDIT.md` 的 `#04-01-M-01..05`。**全文使用 CRLF 换行**(dev.md「换行使用 CRLF 格式」);Task 2/3 追加内容同样保持 CRLF,不得出现孤立 `LF`。
(2) 写 `## 背景`:D-17(对账暴露的主要失配点在勇士移动;下一步先接口探索并找出缺失接口,再适配)、本步只读、明确本步不实施适配、不裁决缺失接口(D-13)、不涉 `HeroRendering` 渲染状态(D-18)与 `core.*`(D-12)。同时写明范围的**另一侧**:勇士移动契约与移动钩子(`IObjectMover` / `IHeroMover` / `IHeroLocationHooks` / `IHeroFollowersControllerHooks` / `IHeroStateHooks`,`onMoveHero` / `onJumpHero` / `onTurnHero` / `onStartMove` / `onEndMove` 等)在本步范围内;`IHeroRendering` / `hero.image` 贴图别名 / `onSetImage` / `onSetAlpha` / `onSetFollowerAlpha` 不在范围内(D-18)。
(3) 写 `## 方法`(步骤 (0) 记录的「扫描前基线」bullet 也在本节):枚举两个面 ——(a)**数据端现行接口面** = `data-common` 移动器族(`IObjectMovable` / `IObjectMover` / `IObjectMoverHooks` / `IMoverController` / `ObjectMoveStep` 各族 / `ObjectAnimDirection`)+ `data-base/hero` 位置移动族(`IHeroState` / `IHeroLocation` / `IHeroMover` / `IHeroLocationHooks` / `IHeroFollowersControllerHooks` / `IHeroStateHooks` / `HeroMover`)+ 各 `index.ts` barrel 实际导出;(b)**渲染端勇士移动使用面** = `render/map/extension/{hero,types,manager}.ts` + `action/move.ts` + `client.ts` 接线点,`render/map/{moving,status,renderer}.ts` 仅作移动器契约参考(不登记为独立对账对象)。判定三态固定为 **匹配 / 渲染端需改 / 数据端缺失**;带 `// @ts-expect-error 需要重构` 的 import 一律先归「渲染端需改」,不归「数据端缺失」。证据纪律:每条含 `接口/成员名 + 两侧 file:line`;静态阅读不能定论者进「未能从阅读确定(未猜测)」,不写成缺失或错配。允许按 D-09 派只读子代理(仅回传文本、不得写文件)。
(4) 补齐 8 个二级标题(`## 背景` 与 `## 方法` 已在前两步写入,此处补齐其余 6 个占位,其后 Task 2/3 在同一骨架上横向补齐,不再改结构):`## 背景` / `## 方法` / `## 数据端现行接口` / `## 渲染端现状` / `## 逐成员对账(匹配 / 渲染端需改 / 数据端缺失)` / `## 缺失接口候选` / `## 未能从阅读确定(未猜测)` / `## 处置`。
(5) **端到端切片(本任务唯一实测对账行)**:以 `MapHeroRenderer` 构造期的钩子注册为对象,从渲染端 `packages-user/client-modules/src/render/map/extension/hero.ts:81`(`hero.addHook(new MapHeroHook(this))`)与 :457-516(`MapHeroHook implements Partial<IHeroMoveControllerHooks>`)出发,打开数据端基准核对。**成员范围**:只登记移动/位置/跟随者钩子 —— `onSetPosition` / `onTurnHero` / `onStartMove` / `onMoveHero` / `onEndMove` / `onJumpHero` / `onAddFollower` / `onRemoveFollower` / `onRemoveAllFollowers`;`onSetImage` / `onSetAlpha` / `onSetFollowerAlpha` 属 `HeroRendering` 贴图/alpha 子系统,按 D-18 **排除**(不写对账行,仅在范围排除说明中提及)。数据端核对:`addHook` 来自 `IHookable`(经 `packages-user/data-common/src/common/mover.ts:235` 的 `IObjectMover<T> extends IHookable<...>` 承接),`onSetPos` 在 `IHeroLocationHooks`(`packages-user/data-base/src/hero/types.ts:283`)与 `IObjectMoverHooks`(`packages-user/data-common/src/common/mover.ts:216`)各有对位,而 `onSetPosition`/`onStartMove`/`onEndMove`/`onMoveHero`/`onJumpHero`/`onTurnHero`/`onAddFollower`/`onRemoveFollower`/`onRemoveAllFollowers` 在数据端无同名对位。据此在 `## 逐成员对账(...)` 下写表头 `| ID | 成员 | 渲染端锚点 | 数据端锚点 | 对账结论 | 说明 |` 与 `#04-02-R-01` 一行:成员=`IHeroMoveControllerHooks` 钩子族(移动/位置/跟随者子集);渲染端锚点=hero.ts:81,457-516;数据端锚点=`data-common/src/common/mover.ts:166,216` 与 `data-base/src/hero/types.ts:277,283`(逐项写「有对位」或「无对位,最接近:<file:line>」);结论=`渲染端需改`(随 `IHeroMoveControllerHooks` 整体重构);说明中注明贴图/alpha 钩子按 D-18 不在本行范围。同时在 `## 数据端现行接口` 与 `## 渲染端现状` 各写至少 1 条带 `file:line` 的最小条目,证明三段相互可达。
(6) 写 `## 处置` 初稿:本步只读、未改任何生产代码;被查渲染端包相对「扫描前基线」零新增改动(用户既有 `packages-user/client-base/src/load/**` 改动保持原样,不回滚 / 不暂存 / 不提交);适配工作(04-03+)不在本 run 规划;明确 `HeroRendering` 渲染状态(D-18:`IHeroRendering` / `hero.image` 贴图别名 / `onSetImage` / `onSetAlpha` / `onSetFollowerAlpha`)与 `core.*`(D-12)为排除项,并说明勇士移动契约与移动钩子在范围内;缺失接口候选须经用户逐步裁决(D-13);`getHeroStatusOn` 类裸函数(D-11)不属本增量范围。
(7) 全程不得修改任何被查源码(含用户既有的 `packages-user/client-base/src/load/**` 未提交改动);本任务只写该交付文档一个文件。
</action>
<reversibility rating="reversible">交付文档是只读探索的记录,可随时修订或废弃;它不引入生产代码、公共接口或契约约束,因此这一步的任何取舍都不难回退。</reversibility>
<verify>
<automated>node -e "const fs=require('fs');const p='.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md';const t=fs.readFileSync(p,'utf8');const need=['## \u80cc\u666f','## \u6570\u636e\u7aef\u73b0\u884c\u63a5\u53e3','## \u6e32\u67d3\u7aef\u73b0\u72b6','## \u9010\u6210\u5458\u5bf9\u8d26\uff08\u5339\u914d / \u6e32\u67d3\u7aef\u9700\u6539 / \u6570\u636e\u7aef\u7f3a\u5931\uff09','## \u7f3a\u5931\u63a5\u53e3\u5019\u9009','## \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-02-R-\d+\s*\|/.test(l));if(rows.length<1){console.error('NO RECON ROWS');process.exit(1)}const bad=rows.filter(l=>!/client-(base|modules)\/[^\s|]*:\d+/.test(l)||!/data-(common|base|state)\/[^\s|]*:\d+/.test(l));if(bad.length>0){console.error('ROW MISSING TWO-SIDED ANCHOR '+bad.length);process.exit(1)}if(t.indexOf('HeroRendering')<0||t.indexOf('core.*')<0){console.error('MISSING SCOPE EXCLUSION');process.exit(1)}console.log('OK headings 7 recRows '+rows.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "MISSING HEADINGS" / "NO RECON ROWS" / "ROW MISSING TWO-SIDED ANCHOR" / "MISSING SCOPE EXCLUSION"</fails_when>
<automated>node -e "const fs=require('fs');const p='.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md';const t=fs.readFileSync(p,'utf8');const need=['## \u80cc\u666f','## \u65b9\u6cd5','## \u6570\u636e\u7aef\u73b0\u884c\u63a5\u53e3','## \u6e32\u67d3\u7aef\u73b0\u72b6','## \u9010\u6210\u5458\u5bf9\u8d26\uff08\u5339\u914d / \u6e32\u67d3\u7aef\u9700\u6539 / \u6570\u636e\u7aef\u7f3a\u5931\uff09','## \u7f3a\u5931\u63a5\u53e3\u5019\u9009','## \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)}if(t.indexOf('\u626b\u63cf\u524d\u57fa\u7ebf')<0){console.error('MISSING BASELINE SNAPSHOT');process.exit(1)}const rows=t.split(/\r?\n/).filter(l=>/^\|\s*#04-02-R-\d+\s*\|/.test(l));if(rows.length<1){console.error('NO RECON ROWS');process.exit(1)}const bad=rows.filter(l=>!/client-(base|modules)\/[^\s|]*:\d+/.test(l)||!/data-(common|base|state)\/[^\s|]*:\d+/.test(l));if(bad.length>0){console.error('ROW MISSING TWO-SIDED ANCHOR '+bad.length);process.exit(1)}if(t.indexOf('HeroRendering')<0||t.indexOf('core.*')<0){console.error('MISSING SCOPE EXCLUSION');process.exit(1)}console.log('OK headings 8 recRows '+rows.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "MISSING HEADINGS" / "MISSING BASELINE SNAPSHOT" / "NO RECON ROWS" / "ROW MISSING TWO-SIDED ANCHOR" / "MISSING SCOPE EXCLUSION"</fails_when>
<automated>node -e "const fs=require('fs');const s=fs.readFileSync('.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md','utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf){console.error('NON-CRLF LINE ENDINGS '+(lf-crlf));process.exit(1)}console.log('OK CRLF lines '+crlf)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "NON-CRLF LINE ENDINGS"(交付文档出现非 CRLF 换行,违反 dev.md「换行使用 CRLF 格式」)</fails_when>
</verify>
<acceptance_criteria>
- 交付文档存在于约定路径,7 个二级标题逐字存在(`## 背景` / `## 数据端现行接口` / `## 渲染端现状` / `## 逐成员对账(匹配 / 渲染端需改 / 数据端缺失)` / `## 缺失接口候选` / `## 未能从阅读确定(未猜测)` / `## 处置`)
- 元信息块 5 行齐备(日期 / 范围 / 性质 / 接口基准 / 输入账本),且「范围」明写排除 `HeroRendering` 与 `core.*`
- 交付文档存在于约定路径,8 个二级标题逐字存在(`## 背景` / `## 方法` / `## 数据端现行接口` / `## 渲染端现状` / `## 逐成员对账(匹配 / 渲染端需改 / 数据端缺失)` / `## 缺失接口候选` / `## 未能从阅读确定(未猜测)` / `## 处置`)
- `## 方法` 含「扫描前基线」条目,原样记录起始 `git status --porcelain -- packages-user/client-base packages-user/client-modules` 输出(规划时为 `client-base/src/load/**` 两条无关用户改动),并明写不破坏该基线
- 元信息块 5 行齐备(日期 / 范围 / 性质 / 接口基准 / 输入账本),且「范围」明写排除 `HeroRendering` 渲染状态(`IHeroRendering` / 贴图别名 / alpha 钩子)与 `core.*`
- `## 逐成员对账(...)` 表列序为 `ID | 成员 | 渲染端锚点 | 数据端锚点 | 对账结论 | 说明`,至少含 1 条 `#04-02-R-*`,同行两侧均有 `file:line` 锚点,结论取自三态
- `#04-02-R-01` 的钩子族判定来自实际打开基准文件核对(hero.ts 的钩子方法名与 `data-common`/`data-base` 现行 hooks 逐项比对),而非照抄 04-RENDER-INTERFACE-AUDIT.md
- `#04-02-R-01` 的钩子族判定来自实际打开基准文件核对(hero.ts 的钩子方法名与 `data-common`/`data-base` 现行 hooks 逐项比对),而非照抄 04-RENDER-INTERFACE-AUDIT.md;且该行**不含** `onSetImage` / `onSetAlpha` / `onSetFollowerAlpha`(按 D-18 排除)
- `## 数据端现行接口` 与 `## 渲染端现状` 各至少 1 条带 `file:line` 条目,三段(数据端 / 渲染端 / 对账)相互可达
- `## 处置` 含 D-12 / D-13 / D-17 / D-18 的明确表述;全文 CRLF;无生产代码改动
- `## 处置` 含 D-12 / D-13 / D-17 / D-18 的明确表述与范围两侧边界;全文 CRLF;相对「扫描前基线」无新增生产代码改动(不回滚用户既有 `client-base/src/load/**` 改动)
</acceptance_criteria>
<done>交付文档骨架与一条勇士移动端到端对账切片贯通,7 段结构 + 双锚点对账行 + CRLF 门禁转绿;Task 2/3 只需在同一模板上横向补齐</done>
<done>交付文档骨架与一条勇士移动端到端对账切片贯通,8 段结构(含 `## 方法`)+ 双锚点对账行 + CRLF 门禁转绿;Task 2/3 只需在同一模板上横向补齐</done>
</task>
<task type="auto">
@ -179,7 +182,7 @@ Output: `04-HERO-MOVER-INTERFACE.md`(本 run 唯一新增交付物;零生产
</read_first>
<action>
(1) **数据端现行接口段**:逐成员登记并给 `file:line` —— `data-common/src/common/mover.ts` 的 `ObjectMoveType` / `ObjectSpecialStep` / `ObjectAnimDirection` / `IObjectMovable` / 各 `IObjectMoveStep*` / `ObjectMoveStep` / `IMoverController` / `IObjectMoverHooks` / `IObjectMover`(含每个方法名与签名,如 `start(): Readonly<IMoverController> | null`、`step/stepFace/forward/backward/speed/face/animDir/tp/jump/push/clear/setPos/setFaceDir/setMoveDir`),以及 `data-base/src/hero/types.ts` 的 `HeroMoveCode` / `IHeroMoveTopHandler` / `IHeroMoveTopImpl` / `IHeroMoverConfig` / `IHeroMover` / `IHeroLocationHooks` / `IHeroLocation` / `IHeroFollower` / `IHeroFollowersControllerHooks` / `IHeroFollowersController` / `IHeroStateHooks` / `IHeroState` 与 `data-base/src/hero/mover.ts` 的 `HeroMover` 公开面(`config` / `getConfig` / `useTopImplementation`)。另单列一条 **barrel 实际导出核对**:写明 `IHeroMover` / `IHeroLocation` / `HeroMover` 等符号实际可经哪个包取得(例如 `HeroMover` 在 `@user/data-base`,`IMoverController` 在 `@user/data-common`,`@user/data-state` barrel 未导出 `HeroMover`/`IMoveController`),每条给 barrel `file:line`。
(2) **渲染端现状段**:逐使用点登记并给 `file:line` —— `extension/hero.ts`(构造器 `:76-102` 的 `hero.addHook`/`hero.x`/`hero.y`/`hero.direction`/`hero.image`、`addHeroMoving :110-132`、`updateHeroTexture :138-163`、`tick :165-189`、`setImage :191-196`、`setAlpha :198-200`、`setPosition :202-207`、`moveEntity :215-238`、`jumpEntity :264-280`、`startMove :282-286`、`waitMoveEnd :295-298`、`stopMove :300-305`、`move :307-314`、`jumpTo :316-339`、`addFollower :341-376`、`removeFollower :378-422`、`removeAllFollowers :424-429`、`setFollowerAlpha :431-435`、`setHeroAnimateDirection :437-439`、`turn :441-450`、`MapHeroHook :457-517`);`extension/types.ts`(`heroMap` 键类型 `:15`、`addHero`/`removeHero` `:26-35`、`IMapHeroRenderer` `:63-159`);`extension/manager.ts`(`:2` import 源、`:18` heroMap、`:26-44` addHero/removeHero);`action/move.ts`(`:3-4` 重构标记与 import、`:11` 类、`:19` `IMoveController`、`:29,:33` `HeroMover` 类型、`:66-67` 全局门控、`:97,:123,:156` `core.status.lockControl`、`:125-127` `oneStep`+`startMove(...)`、`:130-134` `controller.onEnd`、`:137` `on('stepEnd')`、`:169-171` `controller.queue`+`push({type:'dir',value})`、`:180` `off('stepEnd')`);`client.ts:148-153`(被注释的 `addHero(this.hero.mover, layer)` 接线)。段末单列 **`// @ts-expect-error 需要重构` 清单**(文件 + 行号)。`render/map/{moving,status,renderer}.ts` 仅作移动器契约参考,写明其与勇士移动的触点即可,不逐成员展开。
(2) **渲染端现状段**:逐使用点登记并给 `file:line` —— `extension/hero.ts`(构造器 `:76-102` 的 `hero.addHook`/`hero.x`/`hero.y`/`hero.direction`/`hero.image`、`addHeroMoving :110-132`、`updateHeroTexture :138-163`、`tick :165-189`、`setImage :191-196`、`setAlpha :198-200`、`setPosition :202-207`、`moveEntity :215-238`、`jumpEntity :264-280`、`startMove :282-286`、`waitMoveEnd :295-298`、`stopMove :300-305`、`move :307-314`、`jumpTo :316-339`、`addFollower :341-376`、`removeFollower :378-422`、`removeAllFollowers :424-429`、`setFollowerAlpha :431-435`、`setHeroAnimateDirection :437-439`、`turn :441-450`、`MapHeroHook :457-517`);`extension/types.ts`(`heroMap` 键类型 `:15`、`addHero`/`removeHero` `:26-35`、`IMapHeroRenderer` `:63-159`);`extension/manager.ts`(`:2` import 源、`:18` heroMap、`:26-44` addHero/removeHero);`action/move.ts`(`:3-4` 重构标记与 import、`:11` 类、`:19` `IMoveController`、`:29,:33` `HeroMover` 类型、`:66-67` 全局门控、`:97,:123,:156` `core.status.lockControl`、`:125-127` `oneStep`+`startMove(...)`、`:130-134` `controller.onEnd`、`:137` `on('stepEnd')`、`:169-171` `controller.queue`+`push({type:'dir',value})`、`:180` `off('stepEnd')`);`client.ts:148-153`(被注释的 `addHero(this.hero.mover, layer)` 接线)。段末单列 **`// @ts-expect-error 需要重构` 清单**(文件 + 行号)。其中 `setImage` / `setAlpha` / `setFollowerAlpha` / `hero.image` 属 `HeroRendering` 贴图/alpha 子系统,按 D-18 只作**渲染端现状的排除标记**(就地写明「按 D-18 排除,不进入对账与候选」),不得进入 `## 逐成员对账` 或 `## 缺失接口候选`。`render/map/{moving,status,renderer}.ts` 仅作移动器契约参考,写明其与勇士移动的触点即可,不逐成员展开。
(3) **重型整文件读取按 D-09 委派只读子代理**(`hero.ts`、`extension/types.ts`、`action/move.ts`、`data-common/src/common/mover.ts`、`data-base/src/hero/types.ts`、`render/map/moving.ts`/`status.ts`/`renderer.ts`)。子代理只回传事实表(每行:成员名 + 所属 `file:line` + 签名或使用形态),**不得写文件、不贴大段源码**;主执行者据事实表登记并在存疑处按 `file:line` 切片复核。
(4) 只修改交付文档;不得改动任何生产源码,不得新增计划文件。
</action>
@ -194,8 +197,9 @@ Output: `04-HERO-MOVER-INTERFACE.md`(本 run 唯一新增交付物;零生产
- 该段含 barrel 实际导出核对(明确 `HeroMover` / `IMoveController` / `IMoverController` 等符号的实际来源包),并带 `index.ts:line` 锚点
- `## 渲染端现状` 覆盖 `extension/hero.ts`(构造器 + 移动/跳跃/跟随者/朝向/动画方向/钩子注册与回调)、`extension/types.ts`(heroMap 与 `IMapHeroRenderer`)、`extension/manager.ts`(addHero/removeHero/heroMap)、`action/move.ts`(`HeroKeyMover` 全类)、`client.ts` 接线点,每条带 `client-*` `file:line`
- 段末列出全部 `// @ts-expect-error 需要重构` 出现位置(文件 + 行号)
- `HeroRendering` 贴图/alpha 使用点(`setImage` / `setAlpha` / `setFollowerAlpha` / `hero.image`)只作现状段的排除标记,未产生任何对账行或候选(D-18)
- 相关渲染文件 `render/map/{moving,status,renderer}.ts` 仅作为移动器契约参考登记,未被当作独立对账对象
- 被查两渲染端包零改动;未新增任何计划文件;全文 CRLF
- 被查两渲染端包相对「扫描前基线」零新增改动;未新增任何计划文件;全文 CRLF
</acceptance_criteria>
<done>数据端现行接口与渲染端现状两侧清单成员级落位,每条可追溯到 `file:line`,barrel 导出与 `@ts-expect-error` 标记清单齐备</done>
</task>
@ -206,7 +210,7 @@ Output: `04-HERO-MOVER-INTERFACE.md`(本 run 唯一新增交付物;零生产
<read_first>
- .planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md(Task 1/2 产出,做定稿而非重写)
- .planning/phases/04-render-adaptation/04-CONTEXT.md(D-11 / D-12 / D-13 / D-17 / D-18;Deferred Ideas)
- .planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md:30-34(`M-01..05` 归类口径)与 :110-127(「未能从阅读确定」第 2、3、4 条:渲染粒度钩子、`hero.image`、无对位旧状态读)
- .planning/phases/04-render-adaptation/04-RENDER-INTERFACE-AUDIT.md:30-34(`M-01..05` 归类口径)与 :110-127(「未能从阅读确定」第 2、3、4 条:移动钩子候选(其贴图/alpha 子项按 D-18 排除)、`hero.image` 贴图别名边界(D-18 排除,本步不重复登记)、无对位旧状态读)
- packages-user/data-common/src/common/mover.ts:142-233,235-351(`IMoverController` / `IObjectMoverHooks` / `IObjectMover` 逐方法签名,用于三态判定)
- packages-user/data-base/src/hero/types.ts:277-322,399-417,486-506,858-872,889-954(hooks 与 `IHeroState` 成员,用于三态判定)
- packages-user/data-base/src/hero/mover.ts:22-76(`HeroMover` 与 `config`/`getConfig`/`useTopImplementation`)
@ -215,29 +219,29 @@ Output: `04-HERO-MOVER-INTERFACE.md`(本 run 唯一新增交付物;零生产
- dev.md(「双端分离」「其他要求」)
</read_first>
<action>
(1) **逐成员对账段定稿**:为每个渲染端使用成员写一行 `#04-02-R-N`(N 从 2 递增,`R-01` 由 Task 1 占用),结论取 匹配 / 渲染端需改 / 数据端缺失 三态之一,两侧均给 `file:line`(数据端无对位时写「无对位,最接近:<file:line>」)。至少覆盖:`HeroMover` / `IMoveController` 的 import 源与类型名(`action/move.ts:4` vs `data-base/src/hero/mover.ts:22`、`data-common/src/common/mover.ts:142`)→ 渲染端需改;`HeroAnimateDirection`(hero.ts:6,53,97,170,372,437;types.ts:5,147)→ 渲染端需改(改 `ObjectAnimDirection`,`data-common/src/common/mover.ts:40`);`IHeroMoveController`(hero.ts:8,79;types.ts:7,15,27,35;manager.ts:2,18,27,39)→ 渲染端需改(拆为 `IHeroLocation` `data-base/src/hero/types.ts:303` / `IHeroMover` `:399`);`IHeroMoveControllerHooks`(hero.ts:10,61,457)→ 渲染端需改(拆为 `IObjectMoverHooks` `data-common/src/common/mover.ts:166` / `IHeroLocationHooks` `data-base/src/hero/types.ts:277` / `IHeroFollowersControllerHooks` `:486`);`hero.x`/`hero.y`(hero.ts:87-88,117,122,127,129)→ 匹配(`IObjectMovable.x/y` `data-common/src/common/mover.ts:48-51`);`hero.direction`(hero.ts:89,125,193)→ 渲染端需改(`IObjectMover.faceDirection`/`moveDirection` `:243,245`);`hero.image`(hero.ts:115,119,121,143)→ 按既有口径随 `IHeroMoveController` 归「渲染端需改」,并在「未能从阅读确定」交叉引用其与 D-18 的边界;`hero.addHook`(hero.ts:81)→ 匹配(`IHookable`,经 `IObjectMover extends IHookable` `data-common/src/common/mover.ts:235`);`HeroKeyMover` 移动器期望(`action/move.ts:125-127,130-134,137,169-171,180`)逐成员对到 `IObjectMover.step/start`(`:293,350`)与 `IMoverController.push(...ObjectMoveStep[])`(`:152`)、`done`(`:144`)、`onEnd`(`:146`)→ 渲染端需改;`manager.ts:18` `heroMap` 键类型 → 渲染端需改。
(2) **缺失接口候选段**:把渲染端需要而数据端无对位的成员(例如按方向/时长的渲染粒度移动钩子 `onMoveHero(direction, time)` / `onJumpHero(x, y, time, waitFollower)` / `onTurnHero` / `onStartMove` / `onEndMove`,以及 `HeroKeyMover` 依赖的 `oneStep` 与 `controller.queue`)逐条登记为 `### #04-02-G-N:标题` + `- **渲染端需求**:` / `- **数据端现状**:` / `- **待确认**:`;数据端现状必须引用最接近的现行成员 `file:line`(例如 `data-common/src/common/mover.ts:166-233` 的 hooks 方法集 / `:142-164` 的控制器成员集)。**只记录候选**:不设计、不修复、不改数据端接口(D-13)。节末写固定结论行 `**候选结论:** 本步登记 N 项缺失接口候选(未经用户裁决,不视为最终缺失清单,见 D-13)`。
(3) **未能从阅读确定段**:逐条登记无法静态定论项,例如 `hero.image` 贴图别名归属与 D-18 边界、`controller.queue`(`action/move.ts:169`)的运行时形状与 `IMoverController` 的可达性、`state.hero.location` 是否为当前渲染端可取的入口、渲染粒度钩子候选与 04-RENDER-INTERFACE-AUDIT.md 第 2 条候选的重叠关系。不得写成缺失或错配。
(4) **处置段定稿**:本步只读、零生产代码改动;适配工作(04-03+)由用户在本探索结果与候选裁决后另行规划(D-17/D-13);显式排除 `HeroRendering`(D-18)与 `core.*`(D-12);`getHeroStatusOn` 类裸函数(D-11)不属本增量。
(5) **门禁与提交**:运行只读门禁、CRLF 门禁、范围门禁(无 04-03+ 计划文件);随后以 `docs(04-02): 勇士移动接口探索(只读,D-17)` 提交**仅**该交付文档与 `04-02-PLAN.md`,不得提交任何生产源码改动。
(1) **逐成员对账段定稿**:为每个渲染端使用成员写一行 `#04-02-R-N`(N 从 2 递增,`R-01` 由 Task 1 占用),结论取 匹配 / 渲染端需改 / 数据端缺失 三态之一,两侧均给 `file:line`(数据端无对位时写「无对位,最接近:<file:line>」)。至少覆盖:`HeroMover` / `IMoveController` 的 import 源与类型名(`action/move.ts:4` vs `data-base/src/hero/mover.ts:22`、`data-common/src/common/mover.ts:142`)→ 渲染端需改;`HeroAnimateDirection`(hero.ts:6,53,97,170,372,437;types.ts:5,147)→ 渲染端需改(改 `ObjectAnimDirection`,`data-common/src/common/mover.ts:40`);`IHeroMoveController`(hero.ts:8,79;types.ts:7,15,27,35;manager.ts:2,18,27,39)→ 渲染端需改(拆为 `IHeroLocation` `data-base/src/hero/types.ts:303` / `IHeroMover` `:399`);`IHeroMoveControllerHooks`(hero.ts:10,61,457)→ 渲染端需改(拆为 `IObjectMoverHooks` `data-common/src/common/mover.ts:166` / `IHeroLocationHooks` `data-base/src/hero/types.ts:277` / `IHeroFollowersControllerHooks` `:486`);`hero.x`/`hero.y`(hero.ts:87-88,117,122,127,129)→ 匹配(`IObjectMovable.x/y` `data-common/src/common/mover.ts:48-51`);`hero.direction`(hero.ts:89,125,193)→ 渲染端需改(`IObjectMover.faceDirection`/`moveDirection` `:243,245`);`hero.addHook`(hero.ts:81)→ 匹配(`IHookable`,经 `IObjectMover extends IHookable` `data-common/src/common/mover.ts:235`);跟随者增删钩子(`onAddFollower`/`onRemoveFollower`/`onRemoveAllFollowers`,hero.ts:341-429,473-500)→ 渲染端需改(对到 `IHeroFollowersControllerHooks` `data-base/src/hero/types.ts:486-506`);`HeroKeyMover` 移动器期望(`action/move.ts:125-127,130-134,137,169-171,180`)逐成员对到 `IObjectMover.step/start`(`:293,350`)与 `IMoverController.push(...ObjectMoveStep[])`(`:152`)、`done`(`:144`)、`onEnd`(`:146`)→ 渲染端需改;`manager.ts:18` `heroMap` 键类型 → 渲染端需改。**范围外(D-18)**:`hero.image` 贴图别名(hero.ts:115,119,121,143)与 `onSetImage` / `onSetAlpha` / `onSetFollowerAlpha` 属 `HeroRendering` 渲染状态,**不写对账行**(04-01 的 `#04-01-M-05` 已登记该错配,本步不重复),只在「处置」排除说明中提及。
(2) **缺失接口候选段**:把渲染端需要而数据端无对位的成员(例如按方向/时长的**移动语义钩子** `onMoveHero(direction, time)` / `onJumpHero(x, y, time, waitFollower)` / `onTurnHero` / `onStartMove` / `onEndMove`,以及 `HeroKeyMover` 依赖的 `oneStep` 与 `controller.queue`)逐条登记为 `### #04-02-G-N:标题` + `- **渲染端需求**:` / `- **数据端现状**:` / `- **待确认**:`;数据端现状必须引用最接近的现行成员 `file:line`(例如 `data-common/src/common/mover.ts:166-233` 的 hooks 方法集 / `:142-164` 的控制器成员集)。上述移动语义钩子属勇士移动契约,**在范围内**——D-18 的排除只覆盖 `IHeroRendering` / `hero.image` 贴图别名 / `onSetImage` / `onSetAlpha` / `onSetFollowerAlpha`,不得把贴图/alpha 项登记为候选。**只记录候选**:不设计、不修复、不改数据端接口(D-13)。节末写固定结论行 `**候选结论:** 本步登记 N 项缺失接口候选(未经用户裁决,不视为最终缺失清单,见 D-13)`。
(3) **未能从阅读确定段**:逐条登记无法静态定论项,例如 `controller.queue`(`action/move.ts:169`)的运行时形状与 `IMoverController` 的可达性、`state.hero.location` 是否为当前渲染端可取的入口、移动语义钩子候选与 04-RENDER-INTERFACE-AUDIT.md「未能从阅读确定」第 2 条候选的重叠关系(该条中贴图/alpha 子项按 D-18 排除,只保留移动子项在本步登记)。不得写成缺失或错配;**不得把 `hero.image` / 贴图 / alpha 钩子写进本节**(按 D-18 只在「处置」排除说明中提及)。
(4) **处置段定稿**:本步只读、相对「扫描前基线」零新增生产代码改动(用户既有 `packages-user/client-base/src/load/**` 改动保持原样,不回滚 / 不暂存 / 不提交);适配工作(04-03+)由用户在本探索结果与候选裁决后另行规划(D-17/D-13);显式排除 `HeroRendering` 渲染状态(D-18:`IHeroRendering` / `hero.image` 贴图别名 / `onSetImage` / `onSetAlpha` / `onSetFollowerAlpha`)与 `core.*`(D-12),并写明范围的另一侧(勇士移动契约与移动钩子在范围内);`getHeroStatusOn` 类裸函数(D-11)不属本增量。
(5) **门禁与提交**:运行只读门禁(相对「扫描前基线」无**新增**条目)、CRLF 门禁、范围门禁(无 04-03+ 计划文件);随后以 `docs(04-02): 勇士移动接口探索(只读,D-17)` 提交**仅**该交付文档与 `04-02-PLAN.md`,不得提交任何生产源码改动(尤其不得把用户既有的 `client-base/src/load/**` 改动纳入本次提交)。
</action>
<verify>
<automated>node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md','utf8');const rows=t.split(/\r?\n/).filter(l=>/^\|\s*#04-02-R-\d+\s*\|/.test(l));if(rows.length<10){console.error('TOO FEW RECON ROWS '+rows.length);process.exit(1)}const bad=rows.filter(l=>!/client-(base|modules)\/[^\s|]*:\d+/.test(l)||!/data-(common|base|state)\/[^\s|]*:\d+/.test(l));if(bad.length>0){console.error('ROW MISSING TWO-SIDED ANCHOR '+bad.length);process.exit(1)}for(const v of ['\u5339\u914d','\u6e32\u67d3\u7aef\u9700\u6539','\u6570\u636e\u7aef\u7f3a\u5931']){if(t.indexOf(v)<0){console.error('MISSING VERDICT VOCAB '+v);process.exit(1)}}const lines=t.split(/\r?\n/);const gh=lines.map((l,i)=>({l,i})).filter(o=>/^### #04-02-G-\d+/.test(o.l));for(const g of gh){const body=lines.slice(g.i+1,g.i+8).join('\n');if(body.indexOf('\u6e32\u67d3\u7aef\u9700\u6c42')<0||body.indexOf('\u6570\u636e\u7aef\u73b0\u72b6')<0||body.indexOf('\u5f85\u786e\u8ba4')<0){console.error('GAP CANDIDATE MISSING FIELDS');process.exit(1)}}if(t.indexOf('\u5019\u9009\u7ed3\u8bba')<0){console.error('GAP SECTION WITHOUT CONCLUSION');process.exit(1)}console.log('OK recRows '+rows.length+' gapCandidates '+gh.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "TOO FEW RECON ROWS" / "ROW MISSING TWO-SIDED ANCHOR" / "MISSING VERDICT VOCAB" / "GAP CANDIDATE MISSING FIELDS" / "GAP SECTION WITHOUT CONCLUSION"</fails_when>
<automated>node -e "const fs=require('fs');const s=fs.readFileSync('.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md','utf8');const lf=(s.match(/\n/g)||[]).length;const crlf=(s.match(/\r\n/g)||[]).length;if(lf!==crlf){console.error('NON-CRLF LINE ENDINGS '+(lf-crlf));process.exit(1)}console.log('OK CRLF lines '+crlf)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "NON-CRLF LINE ENDINGS"(交付文档出现非 CRLF 换行)</fails_when>
<automated>git status --porcelain -- packages-user/client-base packages-user/client-modules</automated>
<fails_when>stdout 非空(任一被查渲染端包出现改动/新增行)—— 违反只读约束</fails_when>
<automated>node -e "const{execSync}=require('child_process');const out=execSync('git status --porcelain -- packages-user/client-base packages-user/client-modules',{encoding:'utf8'}).replace(/\s+$/,'');const lines=out?out.split(/\r?\n/):[];const allowed=/^.. packages-user\/client-base\/src\/load\//;const extra=lines.filter(l=>!allowed.test(l));if(extra.length>0){console.error('NEW RENDER PKG CHANGES '+extra.length+' '+extra.join(' ; '));process.exit(1)}console.log('OK no new render-pkg changes; pre-existing baseline entries '+lines.length)"</automated>
<fails_when>non-zero exit, 或 stderr 含 "NEW RENDER PKG CHANGES"(相对规划时基线出现了新增的渲染端包改动/新增文件 —— 违反只读约束;规划时既有的 `packages-user/client-base/src/load/**` 用户改动不算,且不得回滚它们)</fails_when>
<automated>node -e "const fs=require('fs');const dir='.planning/phases/04-render-adaptation';const plans=fs.readdirSync(dir).filter(f=>/-PLAN\.md$/.test(f)).sort();const allowed=['04-01-PLAN.md','04-02-PLAN.md'];const extra=plans.filter(p=>allowed.indexOf(p)<0);if(extra.length>0){console.error('UNEXPECTED PLAN FILES '+extra.join(','));process.exit(1)}console.log('OK plan scope '+plans.join(','))"</automated>
<fails_when>non-zero exit, 或 stderr 含 "UNEXPECTED PLAN FILES"(出现了 04-03+ 适配计划文件,即提前规划了未授权的适配工作)</fails_when>
</verify>
<acceptance_criteria>
- 逐成员对账行 ≥ 10 条,每条含两侧 `file:line`;三态词表(匹配 / 渲染端需改 / 数据端缺失)在文档中出现
- `HeroMover`/`IMoveController`、`HeroAnimateDirection`、`IHeroMoveController`、`IHeroMoveControllerHooks`、`hero.x/y/direction/image/addHook`、`HeroKeyMover` 的移动器期望、`manager.ts` heroMap 键类型均已逐成员落位
- 「缺失接口候选」独立成节,每条 `### #04-02-G-*` 含 `渲染端需求` / `数据端现状` / `待确认`,数据端现状带 `file:line`;节末含固定结论行;候选未设计/未修复(D-13)
- 「未能从阅读确定(未猜测)」非空,且 `hero.image`、`controller.queue` 等不确定项只登记不判定
- `## 处置` 含 D-11 / D-12 / D-13 / D-17 / D-18 的明确表述,并明写适配工作(04-03+)未规划
- `git status --porcelain -- packages-user/client-base packages-user/client-modules` 为空;`.planning/phases/04-render-adaptation` 下计划文件仅 `04-01-PLAN.md` 与 `04-02-PLAN.md`;全文 CRLF
- `HeroMover`/`IMoveController`、`HeroAnimateDirection`、`IHeroMoveController`、`IHeroMoveControllerHooks`、`hero.x/y/direction/addHook`、跟随者增删钩子、`HeroKeyMover` 的移动器期望、`manager.ts` heroMap 键类型均已逐成员落位
- 「缺失接口候选」独立成节,每条 `### #04-02-G-*` 含 `渲染端需求` / `数据端现状` / `待确认`,数据端现状带 `file:line`;节末含固定结论行;候选未设计/未修复(D-13);候选只含移动语义 / 移动器成员,不含 `IHeroRendering` / 贴图别名 / `onSetImage` / `onSetAlpha` / `onSetFollowerAlpha`(D-18)
- 「未能从阅读确定(未猜测)」非空,且 `controller.queue` 等不确定项只登记不判定;不出现 `hero.image` / 贴图 / alpha 钩子条目(按 D-18 只在「处置」排除说明中提及)
- `## 处置` 含 D-11 / D-12 / D-13 / D-17 / D-18 的明确表述与范围两侧边界,并明写适配工作(04-03+)未规划
- 相对规划时基线无新增渲染端包改动(`git status --porcelain -- packages-user/client-base packages-user/client-modules` 只含既有 `packages-user/client-base/src/load/**` 条目,且未被回滚/暂存/提交);`.planning/phases/04-render-adaptation` 下计划文件仅 `04-01-PLAN.md` 与 `04-02-PLAN.md`;全文 CRLF
- 交付文档已以 `docs(04-02)` 提交,提交内容仅含该文档与 `04-02-PLAN.md`
</acceptance_criteria>
<done>逐成员三态对账、缺失接口候选、未确定与处置全部定稿;只读/CRLF/范围门禁全绿;交付文档已提交,用户可据此裁决缺失候选并规划后续勇士移动适配</done>
@ -252,18 +256,18 @@ Output: `04-HERO-MOVER-INTERFACE.md`(本 run 唯一新增交付物;零生产
|----------|-------------|
| 数据端接口基准 → 探索文档 | 只读引用 `data-common` / `data-base` / `data-state` 的接口与 barrel;基准不变,仅记录差异 |
| 渲染端源码 → 探索文档 | 只读引用 `client-base` / `client-modules`;不得产生任何写回 |
| 执行者 → 仓库工作树 | 本 run 唯一允许写入的交付文件是探索文档;被查渲染端包必须保持零改动 |
| 执行者 → 仓库工作树 | 本 run 唯一允许写入的交付文件是探索文档;被查渲染端包相对规划时基线必须零**新增**改动(规划时既有的 `client-base/src/load/**` 用户改动原样保留、不回滚) |
| 缺失接口候选 → 用户裁决/后续适配 | 候选被下游用户与适配计划消费;把「渲染端需改」误报为「数据端缺失」会诱导错误的数据端改动(D-13) |
| 探索范围 → Phase 5 / 后续增量 | 把 `HeroRendering`(D-18)或 `core.*`(D-12)混入本增量会扩大范围并污染阶段边界 |
| 探索范围 → Phase 5 / 后续增量 | 把 `HeroRendering` 渲染状态(D-18)或 `core.*`(D-12)混入本增量会扩大范围并污染阶段边界;反之把勇士移动契约/移动钩子误排除会漏掉本次主要失配点 |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|-----------|----------|-----------|----------|-------------|-----------------|
| T-04-02-01 | Tampering | 被查渲染端包 `packages-user/client-base` / `client-modules` | high | mitigate | Task 1–3 均为只读;Task 3 门禁 `git status --porcelain -- packages-user/client-base packages-user/client-modules` 必须为空,非空即失败 |
| T-04-02-01 | Tampering | 被查渲染端包 `packages-user/client-base` / `client-modules` | high | mitigate | Task 1 先记录「扫描前基线」(规划时既有、与勇士移动无关的用户改动 `client-base/src/load/**`);Task 1–3 均为只读;Task 3 门禁断言相对基线**无新增条目**(仅放行 `packages-user/client-base/src/load/` 下条目),出现任何其它条目即失败;不得回滚/暂存/提交用户既有改动 |
| T-04-02-02 | Repudiation | 对账行(无锚点则不可追溯) | medium | mitigate | 每条 `#04-02-R-*` 强制含两侧 `file:line`;由 Task 1/3 的 `<verify>` 门禁机器判定(两侧锚点正则) |
| T-04-02-03 | Spoofing | 「缺失接口候选」段 | high | mitigate | 每条候选必须含 `渲染端需求` / `数据端现状` / `待确认` 三段且数据端现状带 `file:line`;节末固定结论行;门禁机器判定;明写「未经用户裁决不视为最终清单」(D-13) |
| T-04-02-04 | Elevation of Privilege | 探索范围(`HeroRendering` / `core.*` / 双布局 / legacy 移植) | medium | mitigate | D-12/D-18 为显式排除项;文中排除说明与「处置」必须出现 `HeroRendering` 与 `core.*`;范围门禁禁止产生 04-03+ 计划文件 |
| T-04-02-04 | Elevation of Privilege | 探索范围(`HeroRendering` 渲染状态 / `core.*` / 双布局 / legacy 移植) | medium | mitigate | D-12/D-18 为显式排除项且边界写明(排除 `IHeroRendering`/贴图别名/alpha 钩子,保留移动契约与移动钩子);文中排除说明与「处置」必须出现 `HeroRendering` 与 `core.*`;范围门禁禁止产生 04-03+ 计划文件 |
| T-04-02-05 | Information Disclosure | 探索文档引用的源码行与接口名 | low | accept | 记录只含接口名、文件行号与成员签名,不含凭据/密钥 |
| T-04-02-06 | Denial of Service | 只读子代理越权写入(D-09) | low | mitigate | Task 2 明确子代理仅回传事实表、不写文件、不贴大段源码;最终由主执行者只读门禁统一验证 |
| T-04-02-SC | Tampering | npm / pnpm 依赖安装 | high | mitigate | 本计划零依赖变更、不修改任何 `package.json`;出现安装需求即暂停并要求用户确认包合法性(blocking human checkpoint),不得自动继续 |
@ -271,21 +275,21 @@ Output: `04-HERO-MOVER-INTERFACE.md`(本 run 唯一新增交付物;零生产
</threat_model>
<verification>
1. 结构门禁(Task 1):探索文档 7 个二级标题逐字存在;至少 1 条 `#04-02-R-*` 双锚点对账行;文档含 `HeroRendering` 与 `core.*` 排除说明。
1. 结构门禁(Task 1):探索文档 8 个二级标题(含 `## 方法`)逐字存在;`## 方法` 含「扫描前基线」条目;至少 1 条 `#04-02-R-*` 双锚点对账行;文档含 `HeroRendering` 与 `core.*` 排除说明。
2. CRLF 门禁(Task 1/2/3):文档无孤立 `LF`,全文 CRLF(dev.md「换行使用 CRLF 格式」)。
3. 两侧清单门禁(Task 2):数据端段 ≥ 8 个 `data-*` `file:line` 锚点;渲染端段 ≥ 12 个 `client-*` `file:line` 锚点;含 `@ts-expect-error` 标记清单与 `index.ts:line` barrel 导出核对。
4. 对账完整性门禁(Task 3):`#04-02-R-*` ≥ 10 条且每条两侧锚点;三态词表齐备;每条 `### #04-02-G-*` 含 `渲染端需求` / `数据端现状` / `待确认`;`候选结论` 行存在。
5. 只读门禁(Task 3):`git status --porcelain -- packages-user/client-base packages-user/client-modules` 输出为空。
3. 两侧清单门禁(Task 2):数据端段 ≥ 8 个 `data-*` `file:line` 锚点;渲染端段 ≥ 12 个 `client-*` `file:line` 锚点;含 `@ts-expect-error` 标记清单与 `index.ts:line` barrel 导出核对;贴图/alpha 使用点只作 D-18 排除标记。
4. 对账完整性门禁(Task 3):`#04-02-R-*` ≥ 10 条且每条两侧锚点;三态词表齐备;每条 `### #04-02-G-*` 含 `渲染端需求` / `数据端现状` / `待确认`;`候选结论` 行存在;对账行与候选均不含 `IHeroRendering` / `hero.image` / `onSetImage` / `onSetAlpha` / `onSetFollowerAlpha`(D-18)。
5. 只读门禁(Task 3):`git status --porcelain -- packages-user/client-base packages-user/client-modules` 相对规划时基线**无新增条目**(仅既有 `packages-user/client-base/src/load/**` 条目被放行;不回滚它们)。
6. 范围门禁(Task 3):`.planning/phases/04-render-adaptation` 下计划文件仅 `04-01-PLAN.md` 与 `04-02-PLAN.md`(未规划 04-03+ 适配)。
7. 人工核对:「未能从阅读确定(未猜测)」的逐条理由;`## 处置` 对 D-11/D-12/D-13/D-17/D-18 的表述;缺失候选是否确实只记录未设计。
8. 提交:`docs(04-02)` 仅含探索文档与 `04-02-PLAN.md`。
7. 人工核对:「未能从阅读确定(未猜测)」的逐条理由;`## 处置` 对 D-11/D-12/D-13/D-17/D-18 的表述与范围两侧边界;缺失候选是否确实只记录未设计。
8. 提交:`docs(04-02)` 仅含探索文档与 `04-02-PLAN.md`(不含用户既有的 `client-base/src/load/**` 改动)。
</verification>
<success_criteria>
- `.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md` 按 7 段骨架产出,成员级覆盖数据端现行接口与渲染端现状,逐成员三态对账(匹配 / 渲染端需改 / 数据端缺失)每条两侧 `file:line` 可追溯(D-05/D-17)。
- `.planning/phases/04-render-adaptation/04-HERO-MOVER-INTERFACE.md` 按 8 段骨架(含 `## 方法`,其中含「扫描前基线」)产出,成员级覆盖数据端现行接口与渲染端现状,逐成员三态对账(匹配 / 渲染端需改 / 数据端缺失)每条两侧 `file:line` 可追溯(D-05/D-17)。
- 缺失接口候选独立成节、含「渲染端需求 / 数据端现状 / 待确认」,且只记录不设计不修复、未经用户裁决不视为最终清单(D-13)。
- `HeroRendering`(D-18)与 `core.*`(D-12)被显式排除,未产生对账行或候选;`getHeroStatusOn` 类裸函数(D-11)不属本增量。
- 被查两个渲染端包零改动;本 run 只新增探索文档一个交付文件,未规划、未实施任何适配工作(04-03+)。
- `HeroRendering` **渲染状态**(`IHeroRendering` / `hero.image` 贴图别名 / `onSetImage` / `onSetAlpha` / `onSetFollowerAlpha`,D-18)与 `core.*`(D-12)被显式排除,未产生对账行或候选;勇士移动契约与移动钩子(`onMoveHero` / `onJumpHero` / `onTurnHero` / `onStartMove` / `onEndMove` 等)在范围内照常登记;`getHeroStatusOn` 类裸函数(D-11)不属本增量。
- 被查两个渲染端包相对规划时基线零**新增**改动(用户既有 `packages-user/client-base/src/load/**` 改动未被回滚/暂存/提交);本 run 只新增探索文档一个交付文件,未规划、未实施任何适配工作(04-03+)。
- 静态阅读无法定论的项进入「未能从阅读确定(未猜测)」而非写成缺失/错配;全文 CRLF。
- 用户可据此裁决缺失候选并规划后续勇士移动适配。
</success_criteria>