diff --git a/.planning/phases/04-render-adaptation/04-04-PLAN.md b/.planning/phases/04-render-adaptation/04-04-PLAN.md index c51390c..bd59349 100644 --- a/.planning/phases/04-render-adaptation/04-04-PLAN.md +++ b/.planning/phases/04-render-adaptation/04-04-PLAN.md @@ -23,7 +23,7 @@ must_haves: truths: - "本计划的**第一个 block 点**是 Task 0 的 `blocking-human` 汇报关卡:先向用户汇报要解决的问题(04-03 人工审查反馈 D-24..D-28)、大致解决方案(含逐调用替换映射表)、**拟新增 / 删除 / 改写的成员命名清单**(两个钩子类名 + `IFaceHandler` 构造器参数名 + `onSetPos` 归属 + D-18/D-22 多余方法归属)、范围边界与禁令、验证方式与运行时不可得的诚实降级,然后必须**停下等待**用户明确回复「可以执行」四个字;收到该回复前不得执行任何任务、不得派发任何执行子代理。无论触发方式(`/gsd-execute-phase`、`/gsd-manager`、`/gsd-autonomous`、子代理/执行器派发)或配置模式(`yolo`、`workflow.auto_advance`)如何,均不得自动放行(AGENTS.md)" - "**D-24 落地**:`hero.ts` 中不再出现已弃用工具 `getFaceMovement` / `degradeFace` / `nextFaceDirection`(词边界判定),改为 `IFaceHandler` 的 `movement(dir)` / `degrade(dir)` / `next(dir)`;`MapHeroRenderer` 构造器新增 `IFaceHandler` 参数,并在 `manager.ts` 的 `new MapHeroRenderer(...)` 处由 `state.mover.faceHandler` 注入(`IObjectMover.faceHandler`,`data-common/src/common/mover.ts:251`),不使用全局 `state` 取朝向处理器(D-23)" - - "**D-24 的 `degradeFace(dir, FaceDirection.Down)` 回退细节已显式处理**(`hero.ts:357` 的 `addFollower`):`faceHandler.degrade(dir)` 之后当结果为 `FaceDirection.Unknown` 时显式回退为 `FaceDirection.Down`(`IFaceHandler.degrade` 自身对 Unknown 返回 Unknown),与旧 helper 的 `unknown` 参数逐字等价;该处理已在 Task 0 汇报" + - "**D-24 的 `degradeFace(dir, FaceDirection.Down)` 回退细节已显式处理**(`hero.ts:357` 的 `addFollower`):`faceHandler.degrade(dir)` 之后当结果为 `FaceDirection.Unknown` 时显式回退为 `FaceDirection.Down`(`IFaceHandler.degrade` 自身对 Unknown 返回 Unknown);**对 `Unknown` 的回退与旧 helper 的 `unknown` 形参等价,但注入的 `Dir8FaceHandler.degrade` 对斜向输入是恒等**(旧 helper 会折叠为正交:`faceManager.ts:95-97` vs `utils.ts:41-52`),该差异已在 Task 0 汇报并由用户裁定" - "**D-25 落地**:`MapHeroHook` 拆为**两个内部类**(`MapHeroLocationHook` 实现 `Partial`,`MapHeroMoverHook` 实现 `Partial>`),各自用 `hero.addHook(...)` / `hero.mover.addHook(...)` 注册到自己的 hookable 并各持一个 `IHookController` 字段(沿用既有 `locationController` / `moverController`);`onSetPos` **只在 `MapHeroLocationHook` 上实现一次**(`mover.setPos` 会先 `tile.setPos`,location 触发面是超集,见 `mover.ts:522-527`),因此同一次定位不会触发两次;`onSetPos` **无条件** `this.hero.setPosition(x, y)`,**不存在** `mover.moving` 守卫" - "**D-26 落地**:`MapHeroMoverHook.onStepEnd` 的 `ObjectMoveType.AnimDir` 分支不再跳过——除开头既有的 `mover.currAnimDir` 设置外,该分支按本步的 `step.dir`(`IObjectMoveAnimDir.dir: ObjectAnimDirection`,`mover.ts:103-108`)调用 `setHeroAnimateDirection`" - "**D-27 落地(范围仅限 hero.ts)**:`hero.ts` 不再出现 `mutate-animate`(词边界判定),改为从 `@motajs/animate` 引入 `ExcitationCurve2D`(`packages/animate/src/types.ts:12`,barrel `packages/animate/src/index.ts:4`),`generateJumpFn` 返回类型由 `TimingFn<2>` 改为 `ExcitationCurve2D`;二者结构等价(均为 `(progress: number) => [number, number]`,见 `node_modules/mutate-animate/index.d.ts:12-14`),故 `IMovingBlock.moveRelative(curve: TimingFn<2>, ...)`(`render/map/moving.ts:162-166`)仍可接收;**`moving.ts` / `maps/types.ts` / 其它文件的存量 `mutate-animate` 使用一律不动**" @@ -31,7 +31,7 @@ must_haves: - "**D-18 / D-22 成员逐字保留**:`setImage` / `setAlpha` / `setFollowerAlpha` / `addFollower` / `removeFollower` / `removeAllFollowers` 与钩子方法 `onSetImage` / `onSetAlpha` / `onSetFollowerAlpha` / `onAddFollower` / `onRemoveFollower` / `onRemoveAllFollowers` 均不被删除、不被改写语义(贴图 / 不透明度按 D-18 待用户先改数据端;跟随者按 D-22 暂缓)" - "**契约面零变更**:`types.ts` **不被修改**(`IMapHeroRenderer` / `IMapExtensionManager` 的成员集合沿用 04-03 收口结果,本计划不再动它);无任何新增公共接口成员;D-24 的新参数只出现在 `MapHeroRenderer` 构造器(不在任何接口里)" - "**范围外零改动**:`extension/types.ts`、`action/move.ts` 与全部移动控制 API(D-19)、`core.*` / legacy(D-12)、`getHeroStatusOn` 类裸函数(D-11)、任何数据端文件与 `packages/**` 均未被本计划改动;`mutate-animate` 的其余存量使用(D-27 范围外)一动不动" - - "**验证可运行且不臆造通过**:改动前先记录 `pnpm exec vue-tsc --noEmit` 的 `client-modules` 基线错误集(当日实际值,逐行原样入 SUMMARY);改动后该错误集必须与基线**完全一致**(不多不少),且三个在范围文件命中 0 条类型错误;`pnpm exec eslint` 对两个被改文件 0 错误;静态门禁(弃用 helper、`mutate-animate`、`IFaceHandler` 注入、两钩子类分别注册、`onSetPos` 无 `moving` 守卫、AnimDir 已处理、D-18/D-22 保留、D-28 保留)、CRLF 门禁、基线感知范围门禁与并发改动基线门禁全部转绿。渲染运行时无测试设施、且 `MapExtensionManager.addHero` 当前**未被接线**(`client.ts:148-152` 已注释),故本计划的实质验证为「编译 + 契约 + 静态」三类机器门禁,另附人工 UAT 代码复核;**不得**以任何无法运行的门禁冒充通过" + - "**验证可运行且不臆造通过**:改动前先记录 `pnpm exec vue-tsc --noEmit` 的 `client-modules` 基线错误集(当日实际值,逐行原样入 SUMMARY);改动后该错误集必须与基线**完全一致**(不多不少),且三个在范围文件命中 0 条类型错误;`pnpm exec eslint` 对两个被改文件 0 错误;静态门禁(弃用 helper、`mutate-animate`、`IFaceHandler` 注入、两钩子类分别注册、`onSetPos` 无 `moving` 守卫、AnimDir 已处理、D-18/D-22 保留、D-28 保留)、CRLF 门禁、基线感知范围门禁与并发改动基线门禁全部转绿。渲染运行时无测试设施、且 `MapExtensionManager.addHero` 当前**未被接线**(`client.ts:151-155` 已注释),故本计划的实质验证为「编译 + 契约 + 静态」三类机器门禁,另附人工 UAT 代码复核;**不得**以任何无法运行的门禁冒充通过" - "用户并发的未提交改动(以 Task 1 起始 `git status --porcelain` 当日实测为准,排除 `packages-user/client-modules/src/render/map/extension/` 与 `.planning/phases/04-render-adaptation/` 两个前缀)在 Task 1 起始记录基线(porcelain 条目 + 各文件内容 sha256 摘要),Task 3 复验逐条与内容未变;**不回滚 / 不暂存 / 不提交 / 不修改**它们。注意:04-03 SUMMARY 记录的旧基线(`client-base/src/load/*` 等)已在 `4e64953 chore: 内部接口改名` 落库,本计划**不沿用旧列表**,一律以 Task 1 当日实测为准" - "REND-01 / REND-02 在本 run 后仍保持 Pending:本计划只落地「04-03 勇士渲染修正」一个子切片,未实施其余渲染适配与移动端 / 桌面端双布局(见 assumptions 的两条 FLAGGED ASSUMPTION)" artifacts: @@ -52,8 +52,8 @@ must_haves: - "**FLAGGED ASSUMPTION(specless fallback)— REND-01 edge unresolved:** 未提供 REND-01(渲染端适配新数据层接口)的验收边界;本计划**不发明**阈值,只交付「04-03 勇士渲染修正」这一个子切片,验收判据取自 04-CONTEXT 的 D-24..D-28 与 `04-HERO-MOVER-INTERFACE.md` 的 `#04-02-R-01..12`。用户须在推进其余渲染适配前补齐 REND-01 的边界判据。" - "**FLAGGED ASSUMPTION(specless fallback)— REND-02 edge unresolved:** 未提供 REND-02(移动端与桌面端双布局)的验收边界;本计划不涉及双布局实现,也不发明其验收阈值。" - "**执行前必须复核(未确定项,不猜):** Task 1 步骤 (0a) 必须在动手前打开当日 HEAD 的基准文件逐条核对:① `IFaceHandler` 七成员、`movement` 返回 `IFaceDescriptor`、`degrade` 对 Unknown 返回 Unknown;② `IObjectMover.faceHandler` 存在且类型为 `IFaceHandler`;③ `IHeroLocation.mover` 为 `IHeroMover` 且 `HeroLocation` 由 `IFaceHandler` 构造 `HeroMover`;④ 三个弃用 helper 仍导出且签名如记录(「待替换物尚在」的核对,不是要求保留它们);⑤ `@motajs/animate` 导出 `ExcitationCurve2D`、`mutate-animate` 的 `TimingFn<2>` 与 `MovingBlock.moveRelative` 的形参形态;⑥ `IObjectMoveAnimDir.dir`、`prepareStep` 对 AnimDir 的处理、`moveProgress` 的钩子时序。任一不同即**停下汇报并修订本计划**,不得按记忆改代码。" - - "**`IFaceHandler` 语义等价性(Task 0 陈述、Task 1 编译佐证):** 弃用 helper 面向八方向全集,而 `hero.mover` 注入的处理器是有限方向组(`docs/dev/hero/hero-mover.md:74` 记载为 `Dir4FaceHandler`,`data-common/src/common/faceManager.ts:169`)。替换后:① `degrade` 与 `degradeFace(dir)`(unknown 默认 Unknown)对 Unknown 同返回 Unknown;② `movement` / `next` 会先 `degrade`,对落在本组内的正交朝向与旧 helper 逐字一致;③ `nextFaceDirection(dir, false, false)`(默认 face8=false)对**对角输入**与 `IFaceHandler.next`(先 degrade 再 90°)不同,但 `hero.ts:453` 的输入 `this.heroEntity.direction` 只可能来自 `getCurrentFaceDirection()` / `turn(...)` / `degrade(...)`,均为本组朝向,故实际不触发差异;若执行当日实测处理器为 `Dir8FaceHandler`,须在 SUMMARY 记录并复核 `next` / `movement` 差异。" - - "**运行时 UAT 不可得(诚实降级):** `MapExtensionManager.addHero` 目前无任何调用点(`client.ts:148-152` 被注释),故勇士渲染拓展在运行时并未启用;本计划不接线 `client.ts`。因此本计划的运行结果无法在渲染环境观察,验收依赖「scoped 类型门禁 + 契约 / 静态门禁 + 人工代码复核」,运行时渲染 UAT 顺延到启用接线的增量。" + - "**`IFaceHandler` 语义映射(Task 0 陈述、Task 1 注入复核 + 编译佐证;不声称逐字等价):** 当日实际装配链路注入的是 **`Dir8FaceHandler`**(不是文档示例里的 `Dir4FaceHandler`):`core.ts:116` `const dir8 = new Dir8FaceHandler()` → `:143` `new HeroState(this, dir8, heroAttribute)` → `state.ts:55` `new HeroLocation(state, defaultLoc, faceHandler)` → `location.ts:42` `new HeroMover(this, faceHandler)` → `hero.mover.faceHandler`(`mover.ts:251`)。`docs/dev/hero/hero-mover.md:74` 的「本样式中为 `Dir4FaceHandler`」是设计文档里的样式描述,**不是**现行装配事实;一律以 `core.ts` 实际装配为准。对 `hero.ts` 实际用到的三个方法逐一说明(Task 0 须原样汇报差异,**不得**声称等价):① **`movement` 等价**——`getFaceMovement` 与 `Dir8FaceHandler.movement` 均覆盖八方向且 `Unknown→{0,0}`(`faceManager.ts:47-57,99-101` ↔ `utils.ts:8-29`),全输入域一致(`:230` / `:364` / `:365`);② **`degrade` 不等价**——`degradeFace(dir, unknown=Unknown)` 把斜向折叠为正交(`LeftUp/LeftDown→Left`、`RightUp/RightDown→Right`,`utils.ts:41-52`),而 `Dir8FaceHandler.degrade` 是**恒等**(`return dir as FaceDirection`,`faceManager.ts:95-97`);正交与 `Unknown` 一致,**斜向输入**下结果不同(`:132` / `:202` / `:357`);③ **`next` 不等价**——`nextFaceDirection(dir, false, false)` 是 90° 顺时针(`Up→Right`,`utils.ts:135-156`),而 `Dir8FaceHandler.next` 走 `DIR8_CW` 是 **45°** 顺时针(`Up→RightUp`,`faceManager.ts:60-69,113-118`);仅 `turn()` **无参**路径(`hero.ts:451-453`)可达,而仓库内 `turn(...)` 的调用点(`hero.ts:506` / `:511` / `:530`)均显式传参,故该分叉当前**无内部调用者**、属潜性差异(契约上仍可被外部以无参调用触达);④ `:357` 的 `degradeFace(last.nextDirection, FaceDirection.Down)` 在 `Dir8` 恒等语义下,斜向 `last.nextDirection` 不再被折叠、仅 `Unknown` 回退为 `Down`——本计划按 D-24 照常替换并在 Task 0 如实汇报该差异,由用户裁定。" + - "**运行时 UAT 不可得(诚实降级):** `MapExtensionManager.addHero` 目前无任何调用点(`client.ts:151-155` 被注释),故勇士渲染拓展在运行时并未启用;本计划不接线 `client.ts`。因此本计划的运行结果无法在渲染环境观察,验收依赖「scoped 类型门禁 + 契约 / 静态门禁 + 人工代码复核」,运行时渲染 UAT 顺延到启用接线的增量。" - "**数据端实现可能被用户并发修改(D-05):** 接口签名视为稳定,实现可变;本计划只读引用数据端接口,不修改任何数据端文件。若执行当日数据端接口签名与规划基线不一致,按 Task 1 步骤 (0a) 停下汇报,不按旧形状适配。" - "**成员命名需用户同意:** 本计划引入 / 删除的渲染端成员名(两个钩子类 `MapHeroLocationHook` / `MapHeroMoverHook`、`MapHeroRenderer` 的新构造器参数 / 字段 `faceHandler`、被删除的类名 `MapHeroHook`,以及 `onSetPos` 与 D-18/D-22 多余方法的归属)须在 Task 0 汇报中获得用户同意后才可落地(AGENTS.md「任何公共、受保护、私有方法、字段等内容的命名必须先向我反馈」);用户未同意即改名为用户指定的写法,或停下修订计划。" prohibitions: @@ -87,10 +87,10 @@ Phase 4 第四个增量 —— **勇士渲染修正:04-03 人工审查反馈 | 旧调用(hero.ts) | 替换 | 说明 | |---|---|---| -| `degradeFace(dir)` `:132` / `:202` | `this.faceHandler.degrade(dir)` | 旧 helper 默认 `unknown = Unknown`,与 `IFaceHandler.degrade` 对 Unknown 返回 Unknown 一致 | -| `degradeFace(dir, FaceDirection.Down)` `:357` | `this.faceHandler.degrade(dir)`,随后 `=== FaceDirection.Unknown ? FaceDirection.Down : 结果` | `IFaceHandler.degrade` 无 `unknown` 形参,Down 回退须显式保留(行为逐字等价) | -| `getFaceMovement(dir)` `:230` / `:364` / `:365` | `this.faceHandler.movement(dir)` | 返回 `IFaceDescriptor { x, y }`,解构用法不变 | -| `nextFaceDirection(dir)` `:453` | `this.faceHandler.next(dir)` | 默认顺时针;旧 helper 的 `face8=false` / `anticlockwise=false` 默认与此一致,`face8` 在本处未被使用 | +| `degradeFace(dir)` `:132` / `:202` | `this.faceHandler.degrade(dir)` | 对 `Unknown` 与正交输入一致;**斜向输入不等价**——旧 helper 折叠为正交,注入的 `Dir8FaceHandler.degrade` 恒等(`faceManager.ts:95-97` vs `utils.ts:41-52`) | +| `degradeFace(dir, FaceDirection.Down)` `:357` | `this.faceHandler.degrade(dir)`,随后 `=== FaceDirection.Unknown ? FaceDirection.Down : 结果` | `IFaceHandler.degrade` 无 `unknown` 形参,Down 回退须显式保留;`Unknown→Down` 与旧 helper 等价,但斜向输入下 `Dir8` 恒等(旧 helper 会折叠),**非逐字等价** | +| `getFaceMovement(dir)` `:230` / `:364` / `:365` | `this.faceHandler.movement(dir)` | 返回 `IFaceDescriptor { x, y }`,解构用法不变;全输入域等价 | +| `nextFaceDirection(dir)` `:453` | `this.faceHandler.next(dir)` | 均为顺时针,但步长不同:旧 helper(`face8=false`)为 90°(`Up→Right`),注入的 `Dir8FaceHandler.next` 为 45°(`Up→RightUp`);仅 `turn()` 无参路径可达,仓库内无内部调用者 | - **D-24 注入**:`MapHeroRenderer` 构造器新增 `IFaceHandler` 参数(拟名 `faceHandler`);`manager.ts` 的 `new MapHeroRenderer(...)` 由 `state.mover.faceHandler` 注入(`IObjectMover.faceHandler`,`data-common/src/common/mover.ts:251`;`state` 是 `addHero` 的 `IHeroLocation` 形参,不是全局单例,符合 D-23)。 - **D-25 拆分**:把 `MapHeroHook` 拆成 `MapHeroLocationHook`(`Partial`;实现 `onSetPos` **且只实现一次**,另承载 D-18/D-22 的非接口多余方法)与 `MapHeroMoverHook`(`Partial>`;实现 `onMoveStart` / `onMoveEnd` / `onStepEnd` / `onSetFaceDir`,**不**实现 `onSetPos`);`hero.addHook(...)` / `hero.mover.addHook(...)` 分别注册,各持 `locationController` / `moverController` 字段;`onSetPos(x, y)` 无条件 `this.hero.setPosition(x, y)`(删除 `mover.moving` 守卫)。 @@ -191,11 +191,11 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 ② **大致解决方案(两个文件)+ 逐调用替换映射表(须逐行汇报)**: - `manager.ts`:`new MapHeroRenderer(this.renderer, layer, state, state.mover.faceHandler)`(第 4 实参为新增)。 - `hero.ts`:构造器新增 `IFaceHandler` 参数并保存为字段;映射表: - `degradeFace(dir)` `:132`/`:202` → `faceHandler.degrade(dir)`; - `degradeFace(dir, FaceDirection.Down)` `:357` → `faceHandler.degrade(dir)` 后 `=== FaceDirection.Unknown ? FaceDirection.Down : 结果`(Down 回退显式保留,行为逐字等价); - `getFaceMovement(dir)` `:230`/`:364`/`:365` → `faceHandler.movement(dir)`(`IFaceDescriptor { x, y }`); - `nextFaceDirection(dir)` `:453` → `faceHandler.next(dir)`(默认顺时针;本处未用 `face8`);随后删除三个弃用 import。 - 说明语义等价性:弃用 helper 面向八方向全集,注入的处理器是有限方向组(`docs/dev/hero/hero-mover.md:74` 记载为 `Dir4FaceHandler`);对 Unknown 二者同返回 Unknown,对落在本组内的正交朝向逐字一致;`nextFaceDirection(..., face8=false)` 仅在对角输入上与 `IFaceHandler.next`(先 degrade 再 90°)不同,而 `:453` 的输入恒为本组朝向,故不触发差异。 + `degradeFace(dir)` `:132`/`:202` → `faceHandler.degrade(dir)`(对 `Unknown` 与正交输入一致;斜向输入不等价——旧 helper 折叠为正交,`Dir8FaceHandler.degrade` 恒等); + `degradeFace(dir, FaceDirection.Down)` `:357` → `faceHandler.degrade(dir)` 后 `=== FaceDirection.Unknown ? FaceDirection.Down : 结果`(Down 回退显式保留;`Unknown→Down` 等价,斜向输入下 `Dir8` 恒等故不等价); + `getFaceMovement(dir)` `:230`/`:364`/`:365` → `faceHandler.movement(dir)`(`IFaceDescriptor { x, y }`;全输入域等价); + `nextFaceDirection(dir)` `:453` → `faceHandler.next(dir)`(均为顺时针,但旧 helper 为 90°、`Dir8FaceHandler.next` 为 45°;仅 `turn()` 无参路径可达,仓库内无内部调用者);随后删除三个弃用 import。 + **须逐条汇报语义映射(不声称等价):** ① 注入的处理器是 **`Dir8FaceHandler`**(装配链 `core.ts:116` `dir8` → `:143` `new HeroState(this, dir8, ...)` → `state.ts:55` → `location.ts:42` → `hero.mover.faceHandler`);`docs/dev/hero/hero-mover.md:74` 的 `Dir4FaceHandler` 只是文档里的样式描述,不是现行装配,**不作为等价依据**。② `movement` 全输入域等价(八方向 + `Unknown→{0,0}`)。③ `degrade` 不等价:`Dir8FaceHandler.degrade` 是恒等(`faceManager.ts:95-97`),旧 `degradeFace` 折叠斜向(`utils.ts:41-52`)——正交与 `Unknown` 一致,斜向不同。④ `next` 不等价:`Dir8FaceHandler.next` 走 `DIR8_CW` 为 45°(`Up→RightUp`,`faceManager.ts:60-69,113-118`),旧 `nextFaceDirection(face8=false)` 为 90°(`Up→Right`,`utils.ts:135-156`)。⑤ 以上差异按 D-24 照常替换,须由用户在关卡上确认接受、或指定补偿方式。 - D-25:把 `MapHeroHook` 拆成 `MapHeroLocationHook`(`Partial`)与 `MapHeroMoverHook`(`Partial>`);前者实现 `onSetPos(x, y)`(无条件 `this.hero.setPosition(x, y)`,**无 `moving` 守卫**)并承载 D-18/D-22 的非接口多余方法;后者实现 `onMoveStart` / `onMoveEnd` / `onStepEnd` / `onSetFaceDir`,**刻意不实现 `onSetPos`**(location 触发面是超集:`mover.setPos` 先 `tile.setPos`,见 `mover.ts:522-527`),从而同一次定位只触发一次;构造体内分别 `hero.addHook(new MapHeroLocationHook(this))` / `hero.mover.addHook(new MapHeroMoverHook(this))` 并各自 `load()`,`destroy()` 各自 `unload()`。 - D-26:`MapHeroMoverHook.onStepEnd` 的 AnimDir 分支改为 `this.hero.setHeroAnimateDirection(step.dir); break;`(开头既有的 `mover.currAnimDir` 设置保留)。 - D-27:`import { ExcitationCurve2D } from '@motajs/animate';` 取代 `import { TimingFn } from 'mutate-animate';`,`generateJumpFn` 返回类型改 `ExcitationCurve2D`;二者结构等价(`(progress: number) => [number, number]`),`moveRelative(curve: TimingFn<2>, ...)` 仍可接收;仅限 `hero.ts`。 @@ -212,7 +212,7 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 ⑤ **范围边界与禁令**:只改 `hero.ts` 与 `manager.ts`;**不改 `types.ts`**(新参数只在构造器里,契约面无需变动);不新增数据端公共接口、不改任何数据端文件;不动 `action/move.ts` 与移动控制(D-19);不动贴图 / 不透明度(D-18)、跟随者(D-22)、`core.*`(D-12)、裸函数(D-11);D-27 不扩散到 `hero.ts` 之外;不删除存量 `state`(D-28);不接线 `client.ts`;不回滚 / 不暂存 / 不提交用户并发未提交改动。 - ⑥ **验证方式与诚实降级(须原样告知)**:渲染端无测试设施,且 `MapExtensionManager.addHero` 当前无调用点(`client.ts:148-152` 被注释),故运行时渲染无法观察。本计划的运行结果 = **scoped 类型门禁**(`pnpm exec vue-tsc --noEmit` 三个在范围文件 0 条,且 `client-modules` 错误集与当日记录基线完全一致)+ **静态门禁**(弃用 helper 清零、`IFaceHandler` 注入、两钩子类分别注册、`onSetPos` 无守卫、AnimDir 已处理、`mutate-animate` 清零且 `@motajs/animate` 就位)+ **D-18/D-22 与 D-28 保留门禁** + **eslint / CRLF 门禁** + **基线感知范围门禁 + 并发改动基线门禁**;另附**人工代码复核**。**不冒充任何无法运行的门禁。** + ⑥ **验证方式与诚实降级(须原样告知)**:渲染端无测试设施,且 `MapExtensionManager.addHero` 当前无调用点(`client.ts:151-155` 被注释),故运行时渲染无法观察。本计划的运行结果 = **scoped 类型门禁**(`pnpm exec vue-tsc --noEmit` 三个在范围文件 0 条,且 `client-modules` 错误集与当日记录基线完全一致)+ **静态门禁**(弃用 helper 清零、`IFaceHandler` 注入、两钩子类分别注册、`onSetPos` 无守卫、AnimDir 已处理、`mutate-animate` 清零且 `@motajs/animate` 就位)+ **D-18/D-22 与 D-28 保留门禁** + **eslint / CRLF 门禁** + **基线感知范围门禁 + 并发改动基线门禁**;另附**人工代码复核**。**不冒充任何无法运行的门禁。** ⑦ **关卡性质(必须原样告知用户)**:无论触发方式(`/gsd-execute-phase`、`/gsd-manager`、`/gsd-autonomous`、子代理/执行器派发)或配置模式(`.planning/config.json` 的 `yolo`、`workflow.auto_advance`)如何,**执行者/子代理都必须在此停下并等待**用户明确回复「可以执行」四个字;未收到这四个字前不得执行任何任务、不得派发执行子代理,也不得以「自动化模式」为由自动放行。 @@ -247,7 +247,7 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 - 用户回复中明确包含「可以执行」四个字 - - 汇报内容涵盖:要解决的问题(D-24..D-28 五条)、逐调用替换映射表(旧 helper 调用行 → `IFaceHandler` 方法 → 语义等价说明)、成员命名清单(新增 `MapHeroLocationHook` / `MapHeroMoverHook` / `faceHandler`;删除 `MapHeroHook`;方法归属)、Down 回退处理、范围边界与禁令、验证方式与运行时不可得的诚实降级 + - 汇报内容涵盖:要解决的问题(D-24..D-28 五条)、逐调用替换映射表(旧 helper 调用行 → `IFaceHandler` 方法 → 语义映射与差异说明,含注入的 `Dir8FaceHandler` 与 `degrade` / `next` 差异)、成员命名清单(新增 `MapHeroLocationHook` / `MapHeroMoverHook` / `faceHandler`;删除 `MapHeroHook`;方法归属)、Down 回退处理、范围边界与禁令、验证方式与运行时不可得的诚实降级 - 汇报中已原样告知关卡性质:执行者/子代理必须停下等待,不因触发方式或 yolo / auto 模式自动放行 用户明确回复「可以执行」(如需改名 / 改归属 / 改回退处理,已在回复中给出);在此之前未执行任何任务、未修改任何文件、未派发任何执行子代理 @@ -261,16 +261,18 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 - packages-user/client-modules/src/render/map/extension/hero.ts(整文件 552 行:import `:1-21`;`addHeroMoving` `:110-140`;`setImage` `:199-206`;`moveEntity` `:225-248`;`generateJumpFn` `:255`;`addFollower` `:351-386`;`turn` `:451-460`;`MapHeroHook` `:468-552`) - packages-user/client-modules/src/render/map/extension/manager.ts(`addHero` `:24-32`) - packages/common/src/utils/types.ts:145-197(`IFaceDescriptor` / `IFaceHandler`:`degrade` / `movement` / `move` / `opposite` / `next` / `mapDirection` / `mapMovement`) - - packages-user/data-common/src/common/utils.ts:1-64(三个弃用 helper 的现行签名与语义,作为替换等价性的对照) + - packages-user/data-common/src/common/utils.ts:1-64(三个弃用 helper 的现行签名与语义,作为替换语义映射的对照,须核对斜向降级与旋转步长差异) - packages-user/data-common/src/common/mover.ts:103-108,166-232,235-251,513-527,646-687(`IObjectMoveAnimDir` / 钩子成员 / `IObjectMover.faceHandler` / `setPos` / `moveProgress` 时序,供 Task 2 参照,本任务只确认 `faceHandler` 来源) - packages-user/data-base/src/hero/types.ts:277-321,399-417(`IHeroLocationHooks` / `IHeroLocation` / `IHeroMover`) - packages-user/data-base/src/hero/location.ts:33-45(`HeroLocation` 以 `IFaceHandler` 构造 `HeroMover`) - - packages-user/data-common/src/common/faceManager.ts:169-202(`Dir4FaceHandler` 的 `degrade` / `movement` / `next` 实现,核对等价性) + - packages-user/data-common/src/common/faceManager.ts:94-127(`Dir8FaceHandler` 的 `degrade`(恒等)/ `movement`(八方向 + `Unknown`)/ `next`(`DIR8_CW` 45°)实现);对照 `:129-202` 的 `Dir4FaceHandler` 以区分两者 + - packages-user/data-state/src/core.ts:112-144(`dir8 = new Dir8FaceHandler()` `:116` → `new HeroState(this, dir8, heroAttribute)` `:143`:当日实际注入处理器的来源,须现场复核) + - packages-user/data-base/src/hero/state.ts:43-63 与 packages-user/data-base/src/hero/mover.ts:38-42(`faceHandler` 由 `HeroState` → `HeroLocation` → `HeroMover` 的装配链) - packages-user/client-modules/src/action/move.ts:1-6(**只读**:确认 D-19 范围外,不得改动) (0) **执行前核对与基线(只读前置,必须先做)**: - (0a) **数据端现行接口复核(消费未确定项,不猜)**:打开当日 HEAD 的基准文件逐条确认,并把结论(每条一行:结论 + `file:line`)写入 `04-04-SUMMARY.md` 的「执行前核对」段:① `IFaceHandler` 七成员(`degrade` / `movement` / `move` / `opposite` / `next` / `mapDirection` / `mapMovement`)存在,`movement(dir)` 返回 `IFaceDescriptor { readonly x; readonly y }`,`degrade` 对 Unknown 返回 Unknown(`packages/common/src/utils/types.ts:152-197`);② `IObjectMover.faceHandler: IFaceHandler` 存在(`data-common/src/common/mover.ts:251`);③ `IHeroMover extends IObjectMover`(`data-base/src/hero/types.ts:399`)且 `IHeroLocation.mover` 类型为 `IHeroMover`(`:303-314`);④ `HeroLocation` 由传入的 `IFaceHandler` 构造 `HeroMover`(`data-base/src/hero/location.ts:33-45`);⑤ 三个弃用 helper 仍导出且签名如记录(`data-common/src/common/utils.ts:8` `getFaceMovement`、`:37` `degradeFace`、`:64` `nextFaceDirection`)——这是「待替换物尚在」的核对;⑥ `hero.ts` 当前 7 处调用行号仍为 `:132`/`:202`/`:230`/`:357`/`:364`/`:365`/`:453`,import 行仍为 `:2`/`:4`/`:7`。**任一不同:立即停止,不写任何代码,向用户汇报差异并修订本计划。** + (0a) **数据端现行接口复核(消费未确定项,不猜)**:打开当日 HEAD 的基准文件逐条确认,并把结论(每条一行:结论 + `file:line`)写入 `04-04-SUMMARY.md` 的「执行前核对」段:① `IFaceHandler` 七成员(`degrade` / `movement` / `move` / `opposite` / `next` / `mapDirection` / `mapMovement`)存在,`movement(dir)` 返回 `IFaceDescriptor { readonly x; readonly y }`,`degrade` 对 Unknown 返回 Unknown(`packages/common/src/utils/types.ts:152-197`);② `IObjectMover.faceHandler: IFaceHandler` 存在(`data-common/src/common/mover.ts:251`);③ `IHeroMover extends IObjectMover`(`data-base/src/hero/types.ts:399`)且 `IHeroLocation.mover` 类型为 `IHeroMover`(`:303-314`);④ `HeroLocation` 由传入的 `IFaceHandler` 构造 `HeroMover`(`data-base/src/hero/location.ts:33-45`);⑤ 三个弃用 helper 仍导出且签名如记录(`data-common/src/common/utils.ts:8` `getFaceMovement`、`:37` `degradeFace`、`:64` `nextFaceDirection`)——这是「待替换物尚在」的核对;⑥ `hero.ts` 当前 7 处调用行号仍为 `:132`/`:202`/`:230`/`:357`/`:364`/`:365`/`:453`,import 行仍为 `:2`/`:4`/`:7`;⑦ **注入处理器复核(Task 0 前提的现场校验)**:打开 `data-state/src/core.ts:112-144`、`data-base/src/hero/state.ts:43-63`、`data-base/src/hero/location.ts:33-45`、`data-base/src/hero/mover.ts:38-42`,确认 `hero.mover.faceHandler` 的当日实际来源;按现状应为 `Dir8FaceHandler`(`core.ts:116` `dir8` → `:143` `new HeroState(this, dir8, ...)`)。把结论(处理器类名 + 装配链 `file:line`)写入 SUMMARY 的「执行前核对」段。**若实测不是 `Dir8FaceHandler`(例如被改为 `Dir4FaceHandler` 或其它实现),立即停止、不写任何代码,向用户汇报并按新处理器复核 `degrade`/`next`/`movement` 差异后修订本计划**(不得按 Task 0 的 `Dir8` 前提继续,也不得沿用文档示例)。**任一不同:立即停止,不写任何代码,向用户汇报差异并修订本计划。** (0b) **并发改动基线**:执行 `node -e "const fs=require('fs'),c=require('crypto'),{execSync}=require('child_process');const keep=l=>!/packages-user\/client-modules\/src\/render\/map\/extension\//.test(l)&&!/\.planning\/phases\/04-render-adaptation\//.test(l);const out=execSync('git status --porcelain',{encoding:'utf8'}).replace(/\s+$/,'');const cur=(out?out.split(/\r?\n/).map(s=>s.replace(/\s+$/,'')):[]).filter(keep);const digest=c.createHash('sha256').update(cur.slice().sort().map(l=>{let h='MISSING';try{h=c.createHash('sha256').update(fs.readFileSync(l.slice(3))).digest('hex')}catch(e){}return l+' '+h}).join('\n')).digest('hex');console.log(cur.join('\n'));console.log('基线摘要 sha256='+digest)"`,把输出**原样**写入 SUMMARY,格式固定为: ``` @@ -291,13 +293,13 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 (2) **`hero.ts` import(D-24 部分)**:从 `@user/data-common` 的 import 中删除 `degradeFace` / `getFaceMovement` / `nextFaceDirection` 三个名字(其余 `FaceDirection` / `IObjectMover` / `IObjectMoverHooks` / `ObjectAnimDirection` / `ObjectMoveStep` / `ObjectMoveType` 本任务全部保留——都有既有使用点);把 `@motajs/common` 的 `import { IHookController, logger }` 扩为 `import { IFaceHandler, IHookController, logger }`。**本任务不要引入本任务用不到的名字**(eslint `@typescript-eslint/no-unused-vars` 为 error 且 `args: 'all'`,未用 import 直接报错;`--fix` 不会删除未使用 import)。 (3) **`MapHeroRenderer` 构造器(D-24 注入)**:在 `readonly hero: IHeroLocation` 之后新增第 4 个参数 `readonly faceHandler: IFaceHandler`(字段名以 Task 0 用户确认为准)。不改构造体既有钩子注册逻辑(本任务**保留**单一 `MapHeroHook` 现状,Task 2 再拆)。 (4) **7 处弃用 helper 替换(逐处对齐,行号为改动前锚点)**: - (4a) `:132` `degradeFace(hero.getCurrentFaceDirection())` → `this.faceHandler.degrade(hero.getCurrentFaceDirection())`(`addHeroMoving`)。 - (4b) `:202` `degradeFace(this.hero.getCurrentFaceDirection())` → `this.faceHandler.degrade(this.hero.getCurrentFaceDirection())`(`setImage`)。 + (4a) `:132` `degradeFace(hero.getCurrentFaceDirection())` → `this.faceHandler.degrade(hero.getCurrentFaceDirection())`(`addHeroMoving`;正交与 `Unknown` 等价,斜向输入下 `Dir8` 恒等 vs 旧 helper 折叠——差异已在 Task 0 汇报)。 + (4b) `:202` `degradeFace(this.hero.getCurrentFaceDirection())` → `this.faceHandler.degrade(this.hero.getCurrentFaceDirection())`(`setImage`;同 4a 的斜向差异)。 (4c) `:230` `const { x: dx, y: dy } = getFaceMovement(direction);` → `const { x: dx, y: dy } = this.faceHandler.movement(direction);`(`moveEntity`)。 - (4d) `:357` `const nowFace = degradeFace(last.nextDirection, FaceDirection.Down);` → 先 `const degraded = this.faceHandler.degrade(last.nextDirection);`,再 `const nowFace = degraded === FaceDirection.Unknown ? FaceDirection.Down : degraded;`(`addFollower`;**Down 回退显式保留**,行为与旧 helper 的 `unknown` 形参逐字等价)。 + (4d) `:357` `const nowFace = degradeFace(last.nextDirection, FaceDirection.Down);` → 先 `const degraded = this.faceHandler.degrade(last.nextDirection);`,再 `const nowFace = degraded === FaceDirection.Unknown ? FaceDirection.Down : degraded;`(`addFollower`;**Down 回退显式保留**,`Unknown→Down` 与旧 helper 的 `unknown` 形参等价;但注入的 `Dir8FaceHandler.degrade` 对斜向 `last.nextDirection` 是恒等(旧 helper 会折叠为正交),斜向输入下不等价——差异已在 Task 0 汇报)。 (4e) `:364` `const { x: dxn, y: dyn } = getFaceMovement(last.nextDirection);` → `this.faceHandler.movement(last.nextDirection)`(变量名与后续用法不变)。 (4f) `:365` `const { x: dx, y: dy } = getFaceMovement(last.direction);` → `this.faceHandler.movement(last.direction)`。 - (4g) `:453` `nextFaceDirection(this.heroEntity.direction)` → `this.faceHandler.next(this.heroEntity.direction)`(`turn`;默认顺时针,与旧 helper 的 `face8=false` / `anticlockwise=false` 默认一致)。 + (4g) `:453` `nextFaceDirection(this.heroEntity.direction)` → `this.faceHandler.next(this.heroEntity.direction)`(`turn` 的无参路径)。**非等价,按 Task 0 汇报的差异执行**:旧 helper `face8=false` 为 90° 顺时针(`Up→Right`),`Dir8FaceHandler.next` 走 `DIR8_CW` 为 45° 顺时针(`Up→RightUp`);仓库内 `turn(...)` 的调用点(`:506`/`:511`/`:530`)均显式传参,该无参路径当前无内部调用者,差异属潜性;替换按 D-24 执行,若用户要求保持 90° 须在 Task 0 裁定。 **不得**改动这些方法里与其无关的分支 / 注释 / 变量名;**不得**改动 `:235`/`:358`/`:417` 的 `state.roleFace.getFaceOf(...)`(D-28 保留)。 (5) **提交**:本任务改动是整体修正的中间态,**不单独提交**;Task 3 完成全部改动后再一次性原子提交(见 Task 3)。若执行环境强制每任务提交,提交信息用 `fix(04-04): inject IFaceHandler into hero renderer`,并与后续任务的提交在本次执行内连续完成。 (6) 全程保持文件 CRLF;改完执行 `pnpm exec eslint --fix` 对这两个文件,再 `pnpm exec eslint` 必须 0 错误。 @@ -388,8 +390,8 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 (3) **范围门禁(基线感知,先跑,防止越界改动)**:把当前 `git status --porcelain` 的**生产路径**(`^packages(-user)?/`)与 SUMMARY 中 `CONCURRENT-BASELINE` 段记录的基线条目做差集;只有**基线之外**、且不属于 `hero.ts` / `manager.ts` 的生产改动才算越界。特别断言 `extension/types.ts`(本计划不改)、`action/move.ts`(D-19)、`client-base/**`、`data-common/**`、`data-base/**`、`data-system/**`、`data-state/**`、`packages/**` 均无基线之外的新改动。若出现基线之外的第三个生产文件改动:停止、汇报、修订计划(不得自行扩面)。 (4) **并发改动基线门禁**:用与 Task 1 步骤 (0b) 完全相同的固定命令重算(排除 `packages-user/client-modules/src/render/map/extension/` 与 `.planning/phases/04-render-adaptation/` 两个前缀),断言 porcelain 条目集合与内容摘要 `sha256` 均与 SUMMARY 的 `CONCURRENT-BASELINE` 段记录一致。 (5) **复跑全部机器门禁**:scoped 类型门禁(三个在范围文件 0 条 + `client-modules` 错误集 == `TYPE-BASELINE`)、静态门禁 A(D-24/D-25/D-26/D-28)、静态门禁 B(D-27 + D-18/D-22 保留 + `IMapHeroRenderer` 契约保持)、`pnpm exec eslint` 对 hero.ts / manager.ts 0 错误、CRLF 门禁。 - (6) **人工复核(写进 SUMMARY 的「人工复核」段,逐条给结论)**:① D-24 的 7 处逐条对位(旧 helper 行 → 新 `faceHandler` 调用)与语义等价性结论(含当日实测注入的处理器是 `Dir4FaceHandler` 还是 `Dir8FaceHandler`);② `:357` 的 Unknown→Down 回退是否与旧 helper 语义一致;③ D-25:两个钩子类是否分别注册到 `hero` / `hero.mover`,`onSetPos` 是否只出现一次且无条件,并说明「`mover.setPos` → `tile.setPos`(location 钩子)→ `onSetPos` 一次」为何不会再重复触发;④ D-26:AnimDir 分支是否按 `step.dir` 设置,`Speed` 是否仍无渲染动作;⑤ D-27:`hero.ts` 是否已无 `mutate-animate`,`ExcitationCurve2D` 是否被 `moveRelative(curve: TimingFn<2>)` 的形参接受(以编译结果为准);⑥ D-28:`state.roleFace.getFaceOf` 三处与 `import { state } from '@user/data-state'` 是否未动;⑦ D-18/D-22 成员是否逐字保留(对照门禁输出);⑧ 是否新增任何公共接口成员(应为零)且 `types.ts` 是否未改;⑨ 运行时 UAT 不可得的理由已写明(`addHero` 未被接线,`client.ts:148-152` 仍为注释)。 - (7) **SUMMARY 定稿**:写 `.planning/phases/04-render-adaptation/04-04-SUMMARY.md`,含:改动两文件的清单与职责;D-24..D-28 的逐条处置对照表(含 7 处替换映射与 Down 回退处理);执行前核对六条结论(带 `file:line`);执行前基线(`CONCURRENT-BASELINE` 段 + `基线摘要 sha256`)与类型门禁基线(`TYPE-BASELINE` 段,逐行原样);全部机器门禁的实际输出摘要;人工复核九条;未实现 / 显式延后项清单(`client.ts` 接线、D-18 贴图来源、D-22 跟随者、D-19 移动控制、D-27 其余存量迁移、REND-02 双布局、其余 ① / ③ 项);以及 REND-01 / REND-02 仍为 Pending 与运行时 UAT 不可得的明确声明。全文 CRLF。 + (6) **人工复核(写进 SUMMARY 的「人工复核」段,逐条给结论)**:① D-24 的 7 处逐条对位(旧 helper 行 → 新 `faceHandler` 调用)与**语义映射结论(非等价声明)**:当日实测注入处理器类名(应为 `Dir8FaceHandler`,装配链 `core.ts:116` → `:143` → `state.ts:55` → `location.ts:42`)、`movement` 全输入域等价、`degrade` 斜向差异(`Dir8` 恒等 vs 旧 helper 折叠)、`next` 45° vs 90° 差异(含 `:357` 斜向不再折叠),并记录用户对差异的裁定;② `:357` 的 Unknown→Down 回退是否与旧 helper 的 `unknown` 语义一致(`Unknown` 一致,斜向按①记录差异);③ D-25:两个钩子类是否分别注册到 `hero` / `hero.mover`,`onSetPos` 是否只出现一次且无条件,并说明「`mover.setPos` → `tile.setPos`(location 钩子)→ `onSetPos` 一次」为何不会再重复触发;④ D-26:AnimDir 分支是否按 `step.dir` 设置,`Speed` 是否仍无渲染动作;⑤ D-27:`hero.ts` 是否已无 `mutate-animate`,`ExcitationCurve2D` 是否被 `moveRelative(curve: TimingFn<2>)` 的形参接受(以编译结果为准);⑥ D-28:`state.roleFace.getFaceOf` 三处与 `import { state } from '@user/data-state'` 是否未动;⑦ D-18/D-22 成员是否逐字保留(对照门禁输出);⑧ 是否新增任何公共接口成员(应为零)且 `types.ts` 是否未改;⑨ 运行时 UAT 不可得的理由已写明(`addHero` 未被接线,`client.ts:151-155` 仍为注释)。 + (7) **SUMMARY 定稿**:写 `.planning/phases/04-render-adaptation/04-04-SUMMARY.md`,含:改动两文件的清单与职责;D-24..D-28 的逐条处置对照表(含 7 处替换映射与 Down 回退处理);执行前核对七条结论(含注入处理器类名与装配链,带 `file:line`);执行前基线(`CONCURRENT-BASELINE` 段 + `基线摘要 sha256`)与类型门禁基线(`TYPE-BASELINE` 段,逐行原样);全部机器门禁的实际输出摘要;人工复核九条;未实现 / 显式延后项清单(`client.ts` 接线、D-18 贴图来源、D-22 跟随者、D-19 移动控制、D-27 其余存量迁移、REND-02 双布局、其余 ① / ③ 项);以及 REND-01 / REND-02 仍为 Pending 与运行时 UAT 不可得的明确声明。全文 CRLF。 (8) **提交 SUMMARY**:`docs(04-04): record execution state`,仅含 `04-04-SUMMARY.md`。 (9) **不做的事**:不接线 `client.ts`、不改 `action/move.ts`、不改 `types.ts`、不新增测试文件(渲染端无测试设施)、不改任何数据端文件、不修任何 ③ 多余旧路径或其它 ① 错配、不迁移 `hero.ts` 之外的 `mutate-animate`。 @@ -408,7 +410,7 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 non-zero exit 或输出含 "error" node -e "const fs=require('fs');const S='packages-user/client-modules/src/render/map/extension/';for(const f of ['hero.ts','manager.ts']){const s=fs.readFileSync(S+f,'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 '+f+' lone-lf='+(lf-crlf));process.exit(1)}}console.log('OK CRLF source files')" non-zero exit, 或 stderr 含 "NON-CRLF LINE ENDINGS"(两个源文件出现孤立 LF,违反 dev.md「换行使用 CRLF 格式」/ `.prettierrc` 的 `endOfLine: crlf`;SUMMARY 不在本机器门禁范围——04-03-SUMMARY 实测为混合行尾,故不对工具写出的 markdown 施加此硬门禁,改由人工复核遵循仓库 markdown 惯例) - node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-04-SUMMARY.md','utf8');const need=['执行前核对','CONCURRENT-BASELINE:START','TYPE-BASELINE:START','人工复核','REND-01','REND-02','Pending','addHero','client.ts:148','@motajs/animate','IFaceHandler','MapHeroLocationHook','MapHeroMoverHook','AnimDir'];const miss=need.filter(x=>!t.includes(x));if(miss.length){console.error('SUMMARY MISSING SECTIONS '+miss.join(','));process.exit(1)}console.log('OK summary completeness')" + node -e "const fs=require('fs');const t=fs.readFileSync('.planning/phases/04-render-adaptation/04-04-SUMMARY.md','utf8');const need=['执行前核对','CONCURRENT-BASELINE:START','TYPE-BASELINE:START','人工复核','REND-01','REND-02','Pending','addHero','client.ts:151','@motajs/animate','IFaceHandler','MapHeroLocationHook','MapHeroMoverHook','AnimDir'];const miss=need.filter(x=>!t.includes(x));if(miss.length){console.error('SUMMARY MISSING SECTIONS '+miss.join(','));process.exit(1)}console.log('OK summary completeness')" non-zero exit, 或 stderr 含 "SUMMARY MISSING SECTIONS"(SUMMARY 缺少执行前核对 / 基线段 / 人工复核 / 需求 Pending 声明 / 运行时不可得说明 / D-24..D-27 关键项) @@ -417,7 +419,7 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 - 范围门禁(基线感知)通过:除两个在范围文件与基线内的并发用户改动外,无其它生产文件改动(`extension/types.ts`、`action/move.ts`、任何数据端包、`client-base`、`packages` 均无基线之外的新改动) - 并发改动基线门禁通过:porcelain 条目集合与内容 `sha256` 均与 SUMMARY 记录一致 - 静态门禁 A(D-24/D-25/D-26/D-28)、静态门禁 B(D-27 + D-18/D-22 保留 + `IMapHeroRenderer` 契约)、scoped 类型门禁(三文件 0 条、`client-modules` 错误集 == `TYPE-BASELINE`)、eslint 0 错误、CRLF 门禁全部转绿 - - `04-04-SUMMARY.md` 含执行前核对六条(带 `file:line`)、`CONCURRENT-BASELINE` 段 + `基线摘要 sha256`、`TYPE-BASELINE` 段、D-24..D-28 处置对照、门禁输出摘要、人工复核九条、未实现 / 延后项清单、REND-01 / REND-02 仍 Pending 与运行时 UAT 不可得的明确说明;并以 `docs(04-04)` 提交(仅含该 SUMMARY) + - `04-04-SUMMARY.md` 含执行前核对七条(含注入处理器类名与装配链,带 `file:line`)、`CONCURRENT-BASELINE` 段 + `基线摘要 sha256`、`TYPE-BASELINE` 段、D-24..D-28 处置对照、门禁输出摘要、人工复核九条、未实现 / 延后项清单、REND-01 / REND-02 仍 Pending 与运行时 UAT 不可得的明确说明;并以 `docs(04-04)` 提交(仅含该 SUMMARY) D-27 落地并原子提交;七类机器门禁全绿、用户并发改动零变化、范围无越界、SUMMARY 定稿并提交;用户可据此继续裁决后续渲染适配增量(贴图 D-18、跟随者 D-22、接线启用、双布局 REND-02) @@ -444,7 +446,7 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 | T-04-04-02 | Repudiation | 钩子拆分 / 命名 / 映射的可追溯性 | medium | mitigate | 逐调用替换映射表与成员命名清单在 Task 0 汇报并经用户确认;Task 2 / Task 3 静态门禁断言旧单一钩子类名不复现、两类分别注册、`onSetPos` 只出现一次且无守卫、AnimDir 已处理;映射与归属写入 SUMMARY 人工复核段 | | T-04-04-03 | Elevation of Privilege | 执行前置关卡(AGENTS.md 要求的第一处 block 点) | high | mitigate | Task 0 为 `checkpoint:decision` + `gate="blocking-human"` 且本计划 `autonomous: false`;Task 0 的 action ⑦/⑧ 与 acceptance_criteria 明示未收到「可以执行」四个字即必须停止、不派发执行子代理 | | T-04-04-04 | Tampering | 用户并发的未提交改动 | high | mitigate | Task 1 步骤 (0b) 记录 porcelain 条目 + 各文件内容 `sha256` 摘要(写入 SUMMARY 的 `CONCURRENT-BASELINE` 段);Task 3 用同一固定命令复算并断言集合与摘要未变;prohibitions 明确不得回滚 / 暂存 / 提交 / 修改 | -| T-04-04-05 | Spoofing | 「编译通过」被当作「运行时可用」 | medium | mitigate | 运行时 UAT 不可得(`addHero` 未被接线)作为 truths/assumptions 明写;SUMMARY 必须含运行时不可得说明(Task 3 门禁断言 `addHero` 与 `client.ts:148` 字样);严禁以不可运行的门禁冒充通过 | +| T-04-04-05 | Spoofing | 「编译通过」被当作「运行时可用」 | medium | mitigate | 运行时 UAT 不可得(`addHero` 未被接线)作为 truths/assumptions 明写;SUMMARY 必须含运行时不可得说明(Task 3 门禁断言 `addHero` 与 `client.ts:151` 字样);严禁以不可运行的门禁冒充通过 | | T-04-04-06 | Elevation of Privilege | 范围蔓延(D-27 范围外的 `mutate-animate`、D-28 误删 `state`、D-18/D-19/D-22、`types.ts`) | medium | mitigate | 范围门禁(基线感知)只放行两个文件;D-18/D-22 保留门禁与 D-28 保留门禁;prohibitions 逐条列明;需要第三个文件时强制停下汇报并修订计划 | | T-04-04-07 | Information Disclosure | 改动引用的接口名、文件行号与类型签名 | low | accept | 改动只含渲染端源码引用,不含凭据 / 密钥 / 用户数据;无需额外缓解 | | T-04-04-SC | Tampering | npm / pnpm 依赖安装 | high | accept | 本计划零依赖变更、不修改 `package.json` / `pnpm-lock.yaml`(`@motajs/animate` 已是 `client-modules` 的 workspace 依赖),Package Legitimacy Gate 不触发;若执行中出现安装需求,立即停止并汇报 | @@ -459,7 +461,7 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 0. 执行前置关卡(Task 0,AGENTS.md 第一个 block 点):向用户汇报「要解决的问题(D-24..D-28)/ 大致解决方案(含逐调用替换映射表)/ 成员命名清单与方法归属 / Down 回退处理 / 范围边界 / 验证方式的诚实降级」后**停下等待**用户明确回复「可以执行」四个字;未收到前不得执行任何任务、不得派发执行子代理,任何触发方式与 `yolo` / `auto_advance` 模式均不得自动放行(人工核对:用户回复原文)。 -1. 执行前核对与基线(Task 1 步骤 0):六条数据端现行接口复核结论(带 `file:line`)写入 SUMMARY;并发改动基线(`CONCURRENT-BASELINE` 段 + `基线摘要 sha256`)与类型门禁基线(`TYPE-BASELINE` 段,逐行原样)写入 SUMMARY;任一与规划不一致即停止并修订计划;**不得沿用 04-03 SUMMARY 的旧并发列表**(已随 `4e64953` 落库)。 +1. 执行前核对与基线(Task 1 步骤 0):七条数据端现行接口与注入处理器复核结论(含 `hero.mover.faceHandler` 实测类名与装配链 `file:line`)写入 SUMMARY;并发改动基线(`CONCURRENT-BASELINE` 段 + `基线摘要 sha256`)与类型门禁基线(`TYPE-BASELINE` 段,逐行原样)写入 SUMMARY;任一与规划不一致即停止并修订计划;**不得沿用 04-03 SUMMARY 的旧并发列表**(已随 `4e64953` 落库)。 2. scoped 类型门禁(Task 1 / 2 / 3):`pnpm exec vue-tsc --noEmit` 过滤到 `render/map/extension/{hero,types,manager}.ts` 必须 0 条诊断;`client-modules` 错误集必须与 SUMMARY 的 `TYPE-BASELINE` 完全一致(集合比较,顺序无关)。整仓其余既有诊断不属本计划,不得以「整仓非 0 退出」判定失败,也不得漏判范围文件的新诊断。 3. 静态门禁 A(Task 1 / 2 / 3):三个在范围文件不出现**独立 token**(词边界)`getFaceMovement` / `degradeFace` / `nextFaceDirection` / `MapHeroHook`;必须出现 `IFaceHandler` / `faceHandler` / `MapHeroLocationHook` / `MapHeroMoverHook` / `hero.addHook` / `hero.mover.addHook` / `locationController` / `moverController` / `setHeroAnimateDirection(step.dir)`;`onSetPos` 方法体内不含 `moving` 且含 `setPosition(`;`hero.ts` 含 `state.roleFace.getFaceOf`(≥3)与 `from '@user/data-state'`。 4. 静态门禁 B(Task 3):`hero.ts` 不含 `mutate-animate`(独立 token)且含 `from '@motajs/animate'` 与 `ExcitationCurve2D`;`hero.ts` 的 D-18/D-22 钩子与实现方法逐字保留;`IMapHeroRenderer` 的 `setImage` / `addFollower` / `removeFollower` / `removeAllFollowers` / `setAlpha` / `setFollowerAlpha` / `destroy` 逐字保留。 @@ -467,7 +469,7 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 6. 并发改动基线门禁(Task 3):排除两个在范围文件与 `.planning/phases/04-render-adaptation/` 后,porcelain 条目集合与内容 `sha256` 与 SUMMARY 的 `CONCURRENT-BASELINE` 段记录一致(用户改动未被回滚 / 暂存 / 提交 / 修改)。 7. 范围门禁(Task 3,基线感知):将当前 `git status --porcelain` 的生产路径(`^packages(-user)?/`)与 `CONCURRENT-BASELINE` 段做差集;只有基线之外、且不属于 `hero.ts` / `manager.ts` 的生产改动才判越界;`extension/types.ts`、`action/move.ts`、数据端各包、`client-base`、`packages` 均无基线之外的新改动。 8. ESLint / CRLF 门禁(Task 1 / 2 / 3):`pnpm exec eslint` 对 `hero.ts` / `manager.ts` 0 错误;两个源文件无孤立 LF(`.prettierrc` 的 `endOfLine: crlf`)。SUMMARY 不施加该硬门禁(04-03-SUMMARY 实测为混合行尾),改由人工复核遵循仓库 markdown 惯例。 -9. 人工复核(Task 3):D-24 的 7 处替换逐条对位且语义等价(含注入处理器实际方向组的记录);Down 回退与旧语义一致;两钩子类分别注册且 `onSetPos` 只出现一次、无条件;AnimDir 按 `step.dir` 设置且 `Speed` 仍 no-op;D-27 仅在 `hero.ts` 生效且 `ExcitationCurve2D` 被编译接受;D-28 未动;D-18/D-22 逐字保留;无新增公共接口成员且 `types.ts` 未改;运行时 UAT 不可得的理由已写明。 +9. 人工复核(Task 3):D-24 的 7 处替换逐条对位且**语义映射与差异已记录(非等价声明)**(含注入处理器实测类名 `Dir8FaceHandler` 与装配链、`movement` 全等、`degrade` 斜向差异、`next` 45° vs 90° 差异);Down 回退的 `Unknown` 语义一致;两钩子类分别注册且 `onSetPos` 只出现一次、无条件;AnimDir 按 `step.dir` 设置且 `Speed` 仍 no-op;D-27 仅在 `hero.ts` 生效且 `ExcitationCurve2D` 被编译接受;D-28 未动;D-18/D-22 逐字保留;无新增公共接口成员且 `types.ts` 未改;运行时 UAT 不可得的理由已写明。 10. 提交:Task 3 的 `fix(04-04)` 仅含 `hero.ts` / `manager.ts`;`docs(04-04)` 仅含 SUMMARY;用户并发改动未被纳入任何提交。 @@ -488,7 +490,7 @@ Output: `hero.ts` / `manager.ts` 的修正改动(原子提交)+ 会话内可 |------|------|------| | 生产源码(修改) | `packages-user/client-modules/src/render/map/extension/hero.ts` | `MapHeroRenderer` 持有注入的 `IFaceHandler` 并以 `degrade` / `movement` / `next` 取代三个弃用 helper(含 `:357` 的 Unknown→Down 显式回退);`MapHeroHook` 拆为 `MapHeroLocationHook` / `MapHeroMoverHook` 两个内部类分别注册;`onSetPos` 无条件且无 `moving` 守卫;`onStepEnd` 的 AnimDir 分支按 `step.dir` 设置动画方向;`mutate-animate` → `@motajs/animate` 的 `ExcitationCurve2D`;D-18 / D-22 / D-28 成员逐字保留 | | 生产源码(修改) | `packages-user/client-modules/src/render/map/extension/manager.ts` | `addHero` 把 `state.mover.faceHandler` 作为第四个实参注入 `new MapHeroRenderer(...)`;其余一字不改 | -| 摘要 | `.planning/phases/04-render-adaptation/04-04-SUMMARY.md` | 执行前核对六条(带 `file:line`)、`CONCURRENT-BASELINE` 段 + `基线摘要 sha256`、`TYPE-BASELINE` 段、D-24..D-28 处置对照与 7 处替换映射、门禁输出摘要、人工复核九条、未实现项清单、REND-01 / REND-02 Pending 声明与运行时 UAT 不可得说明 | +| 摘要 | `.planning/phases/04-render-adaptation/04-04-SUMMARY.md` | 执行前核对七条(含注入处理器实测类名与装配链,带 `file:line`)、`CONCURRENT-BASELINE` 段 + `基线摘要 sha256`、`TYPE-BASELINE` 段、D-24..D-28 处置对照与 7 处替换映射(含 `Dir8FaceHandler` 的 `degrade` / `next` 差异记录)、门禁输出摘要、人工复核九条、未实现项清单、REND-01 / REND-02 Pending 声明与运行时 UAT 不可得说明 | Create `.planning/phases/04-render-adaptation/04-04-SUMMARY.md` when done