fix(02-pathfinding): revise plans for user path/types.ts interface redesign

This commit is contained in:
unanmed 2026-09-09 16:57:17 +08:00
parent 7a011b29c2
commit 9efadc9806
2 changed files with 134 additions and 93 deletions

View File

@ -7,12 +7,12 @@ depends_on: ["02-01"]
files_modified:
- packages-user/data-common/src/common/mover.ts
- packages-user/data-common/src/common/mover.test.ts
- packages-user/data-system/src/pathfinding/types.ts
- packages-user/data-system/src/pathfinding/graph.ts
- packages-user/data-system/src/pathfinding/system.ts
- packages-user/data-system/src/pathfinding/index.ts
- packages-user/data-system/src/pathfinding/graph.test.ts
- packages-user/data-system/src/pathfinding/system.test.ts
- packages-user/data-system/src/path/types.ts
- packages-user/data-system/src/path/graph.ts
- packages-user/data-system/src/path/system.ts
- packages-user/data-system/src/path/index.ts
- packages-user/data-system/src/path/graph.test.ts
- packages-user/data-system/src/path/system.test.ts
- packages-user/data-system/src/index.ts
- packages/common/src/logger.json
autonomous: true
@ -25,40 +25,41 @@ estimate:
must_haves:
truths:
- "mover.ts:651 坐标回写条件按用户拍板结果修复L0 回归 4 用例x 正交/y 正交/斜向/传送)全部翻绿 (D-07)"
- "有向图构建正确:边 = outPass(source) 含方向位且 inPass(dest) 含反向位,单向门 A→B 可行而 B→A 不可行有测试 (D-02)"
- "最小损失搜索满足 D-01默认每格损失 1可注入自定义损失函数返回两点间损失最小路径 (D-01, D-02)"
- "仅取路径 API 存在且不移动任何对象 (D-06)"
- "path/types.ts 的 moveTo/teleportTo 返回类型已按用户授权修正为 `IPathfindingController | null`,除此之外该文件零改动(用户提交 7a011b2user-owned"
- "有向图构建正确:邻域方向由 `useDirGroup``IDirectionMapper.map(group)` 解析(默认 `InternalDirectionGroup.Dir4` 四正交),边可行性由注入的 `IPassPredicate.canPass(IPassCheckHandler)` 判定,单向门 A→B 可行而 B→A 不可行有测试 (D-02)"
- "最小损失搜索满足 D-01默认每格损失 1`PathCostFunction(block: ILayerLocation) => number`),可注入自定义损失函数,返回两点间损失最小的 `IPathfindingStep[]` 步骤序列dir/from/to(D-01, D-02)"
- "仅取路径 API `getPath(target): IPathfindingStep[]` 存在且不移动任何对象 (D-06)"
- "不可达目标按 D-08 情形 2 返回空数组且不移动 (D-08)"
- "回退策略槽位存在:策略函数接收路径及每步到达位置并决策移动方式(瞬移或退为逐步)(D-04, D-05)"
- "绑定面为 IMapLayer 与 IObjectMovable 注入槽位L2 未引用 L3 任何符号 (D-03)"
- "回退策略槽位存在:`PathFallbackPolicy(path) => boolean`true = 必须逐步、禁止瞬移),注入 null 时默认必定回退为逐步寻路;「路径上存在事件即回退」的拍板默认策略由 02-03 在 L3 注入 (D-04, D-05)"
- "绑定面为 `useMapState`/`useMapLayer`/`useMovable` 注入槽位与 `usePassPredicate` 槽位(默认均不可通行)L2 未引用 L3 任何符号 (D-03)"
artifacts:
- "packages-user/data-common/src/common/mover.ts"
- "packages-user/data-common/src/common/mover.test.ts"
- "packages-user/data-system/src/pathfinding/types.ts"
- "packages-user/data-system/src/pathfinding/graph.ts"
- "packages-user/data-system/src/pathfinding/system.ts"
- "packages-user/data-system/src/pathfinding/index.ts"
- "packages-user/data-system/src/pathfinding/graph.test.ts"
- "packages-user/data-system/src/pathfinding/system.test.ts"
- "packages-user/data-system/src/path/types.ts"
- "packages-user/data-system/src/path/graph.ts"
- "packages-user/data-system/src/path/system.ts"
- "packages-user/data-system/src/path/index.ts"
- "packages-user/data-system/src/path/graph.test.ts"
- "packages-user/data-system/src/path/system.test.ts"
- "packages-user/data-system/src/index.ts"
- "packages/common/src/logger.json"
key_links:
- "graph.ts 消费注入的通行性谓词而非 import L3谓词复刻 moverImpl canPass 的掩码语义(事件层恒参与,其余层仅 onlyEvents 参与)"
- "system.ts 的损失/回退/谓词槽位由 02-03 的 L3 接线注入"
- "graph.ts 消费注入的 IPassPredicatecanPass 判边可行性、shouldHit 分类终端/撞击节点)而非 import L3掩码语义谓词由 02-03 Task 1 从 moverImpl 重构为 predicate() 后经 usePassPredicate 注入"
- "system.ts 的损失/回退/谓词槽位由 02-03 的 L3 接线填充hero mover 的 predicate() 注入 usePassPredicate"
assumptions:
- "PATH-01flagged assumptionspec-less probe 未分类):本计划交付自动寻路的纯计算核心(图构建 + 最小损失搜索 + 仅取路径Node 可验证"
- "PATH-02flagged assumptionspec-less probe 未分类):本计划提供的仅取路径 API 即渲染端 Phase 4 将调用的数据端入口之一,点击接线不在本阶段 (D-11)"
prohibitions:
- "不得为寻路引入任何新 npm 依赖Dijkstra 自写,零新包)"
- "不得把渲染端点击处理接进数据端代码 (D-11)"
- "接口签名必须与用户拍板后的草案逐字一致,不得自行更名或改层 (D-07)"
- "path/types.ts 为用户所有commit 7a011b2除用户授权的 moveTo/teleportTo 返回类型修正外不得重写、更名或改层 (D-07)"
---
<objective>
按用户拍板结果修复 L0 mover.ts:651 坐标回写缺陷并翻绿回归测试,然后在 data-systemL2落地寻路核心有向图构建注入谓词)、最小损失搜索(自定义损失、默认每格 1、仅取路径 API、D-08 情形 2 空数组语义、回退策略槽位与移动方式决策
按用户拍板结果修复 L0 mover.ts:651 坐标回写缺陷并翻绿回归测试,然后在 data-systemL2落地寻路核心有向图构建注入 IPassPredicate + useDirGroup 方向组)、最小损失搜索(`PathCostFunction` 自定义损失、默认每格 1、`getPath` 仅取路径步骤序列、D-08 情形 2 空数组语义、`PathFallbackPolicy` 回退策略槽位与移动方式决策。接口签名以用户已提交的 `packages-user/data-system/src/path/types.ts`commit 7a011b2为唯一事实源——本计划仅消费该文件唯一例外是用户授权的 moveTo/teleportTo 返回类型修正
Purpose: 这是全部寻路行为的纯逻辑地基——不移动任何对象,可在 Node 下完整验证L0 缺陷不修则 02-03 的多步逐步移动必然走崩RESEARCH P1
Output: 修复后的 mover.ts + 绿色 L0 回归pathfinding/{types,graph,system,index}.ts 及 co-located 测试。
Output: 修复后的 mover.ts + 绿色 L0 回归path/{graph,system,index}.ts 及 co-located 测试path/types.ts 的授权返回类型修正
</objective>
<execution_context>
@ -74,7 +75,14 @@ Output: 修复后的 mover.ts + 绿色 L0 回归pathfinding/{types,graph,syst
@.planning/phases/02-pathfinding/02-PATTERNS.md
@.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md
# 草案与拍板记录是本计划的接口事实源
# 接口事实源:用户已提交的 path/types.ts草案与拍板记录为语义背景
@packages-user/data-system/src/path/types.ts
@packages/common/src/utils/types.ts
@packages/common/src/utils/dir.ts
# 草案与拍板记录
@.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md
@packages-user/data-common/src/common/mover.ts
@packages-user/data-common/src/store/types.ts
@packages-user/data-base/src/map/types.ts
@ -105,40 +113,44 @@ Output: 修复后的 mover.ts + 绿色 L0 回归pathfinding/{types,graph,syst
</task>
<task type="auto">
<name>Task 2: 有向图构建(注入谓词 + 终端节点 + 边界守卫)</name>
<files>packages-user/data-system/src/pathfinding/graph.ts, packages-user/data-system/src/pathfinding/types.ts, packages-user/data-system/src/pathfinding/graph.test.ts</files>
<read_first>.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-base/src/map/mapLayer.ts, packages-user/data-base/src/map/types.ts, packages-user/data-common/src/store/types.ts, packages-user/data-system/src/combat/mapDamage.ts, dev.md</read_first>
<action>创建 types.ts 与 graph.ts签名与命名以拍板草案为唯一事实源D-07。types.ts损失函数、回退策略、通行性谓词的函数类型别名图节点/邻接的 interface全部 jsDoc 中文注释、对象类型单独 interface、禁 `import type`dev.md。graph.ts`IMapLayer` 转换为有向图——节点为 inMap 内图块;从 A 向方向 d 的边成立当且仅当通行性谓词(source, d, dest) 为真D-02 有向图要求,单向门由掩码不对称产生);谓词为 `useXxx()` 注入槽位PATTERNS Shared Pattern 1 同款L2 默认实现逐字复刻 moverImpl.ts:85-104 + 107-122 的掩码语义(事件层恒参与判定;其余层仅当 `pass.onlyEvents` 为真参与方向→PassBit 映射照 moverImpl.ts:40-53 语义L3 可注入复用 `DefaultHeroMoveTopImpl` 判定的谓词。终端节点canPass 为真但 `eventPass` 为假的图块标记为仅可作路径终点、不可作中间节点RESEARCH Pitfall 3。边界守卫构建与查询入口统一 `IMapLayer.inMap` + `isNil` 判空,非法输入 `logger.warn` 新码warn ≥173本任务在 packages/common/src/logger.json 登记首个本阶段码)后返回空图/空结果。graph.test.ts 用 eventDispatch.test.ts:105-179 的 fixture 模式TileStore.addTile 位掩码 + FaceManager.register + MapState.fromRaw必含用例A→B 可行且 B→A 不可行的单向门Pitfall 4、eventPass=false 终端节点、越界坐标守卫告警。禁 import L3 任何符号(防循环依赖)。</action>
<name>Task 2: 授权的 types.ts 返回类型修正 + 有向图构建(注入 IPassPredicate + useDirGroup + 终端节点 + 边界守卫)</name>
<files>packages-user/data-system/src/path/types.ts, packages-user/data-system/src/path/graph.ts, packages-user/data-system/src/path/graph.test.ts</files>
<read_first>packages-user/data-system/src/path/types.ts, packages/common/src/utils/types.ts, packages/common/src/utils/dir.ts, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-base/src/map/mapLayer.ts, packages-user/data-base/src/map/types.ts, packages-user/data-common/src/store/types.ts, packages-user/data-system/src/combat/mapDamage.ts, dev.md</read_first>
<action>【授权修正,先行】`path/types.ts` 为用户所有commit 7a011b2本任务对它做且仅做一处用户逐字授权的修正`IPathfindingSystem.moveTo` 与 `teleportTo` 的返回类型由 `IPathfindingController` 改为 `IPathfindingController | null`jsdoc 已写明「失败返回 null」与契约对齐。不得改动该文件任何其他内容D-07
【graph.ts】新建 `packages-user/data-system/src/path/graph.ts`(不新建 types.ts——它已由用户提交直接 `import` 其导出:`IPathfindingStep`、`PathCostFunction`、`PathFallbackPolicy`,并从 `@user/data-base` 引入 `IPassPredicate`/`IPassCheckHandler`/`ILayerLocation`,从 `@motajs/common` 引入 `InternalDirectionGroup`)。图构建:节点为 `IMapLayer.inMap` 内图块;邻域由 `useDirGroup(group: number)` 注入的方向组经 `IDirectionMapper.map(group)``@motajs/common` utils 的 `DirectionMapper` 实例L0 合法引入)解析为 `IDirectionDescriptor` 坐标增量,默认组 `InternalDirectionGroup.Dir4`四正交per 拍板);从 A 向描述器 d 的边成立当且仅当注入的 `IPassPredicate.canPass({ currLoc, nextLoc, direction, floorId })` 为真D-02 有向图要求,单向门由谓词判定不对称产生);谓词经 `usePassPredicate(predicate: IPassPredicate | null)` 注入默认null均不可通行——per 用户 types.ts jsdoc掩码语义谓词由 02-03 Task 1 从 moverImpl 重构提供。每条边的 `IPathfindingStep.dir` 取与描述器坐标增量对应的 `FaceDirection`Dir4 四增量与 moverImpl.ts:40-53 directionToPassBit 的 Up/Right/Down/Left 一一对应Dir8 斜向增量对应四个斜向 FaceDirection。终端节点分类canPass 为真但注入谓词 `shouldHit` 为真的图块标记为仅可作路径终点、不可作中间节点RESEARCH Pitfall 3D-08 语义:掩码允许到达且目标 no-pass 才构成撞击/OnTouch 情形,掩码导致的不可达永不触发)。边界守卫:构建与查询入口统一 `IMapLayer.inMap` + `isNil` 判空,非法输入 `logger.warn` 新码warn ≥173本任务在 packages/common/src/logger.json 登记首个本阶段码)后返回空图/空结果。graph.test.ts 用 eventDispatch.test.ts:105-179 的 fixture 模式TileStore.addTile 位掩码 + FaceManager.register + MapState.fromRaw测试内注入复刻 moverImpl 掩码语义的 fixture 谓词(事件层恒参与、其余层仅 `pass.onlyEvents` 参与必含用例A→B 可行且 B→A 不可行的单向门Pitfall 4、shouldHit=true 终端节点、越界坐标守卫告警。禁 import L3 任何符号(防循环依赖)。</action>
<verify>
<automated>pnpm exec vitest run "packages-user/data-system/src/pathfinding/graph.test.ts"</automated>
<automated>pnpm exec vitest run "packages-user/data-system/src/path/graph.test.ts"</automated>
<fails_when>vitest 输出出现 "failed" 字样或非零退出码——单向门、终端节点或边界守卫断言未通过</fails_when>
</verify>
<acceptance_criteria>
- path/types.ts 的 git diff 仅含 moveTo/teleportTo 两处返回类型追加 `| null`无其他变更CLI 断言)
- 单向门用例:同一条边从 A→B 判定可行、从 B→A 判定不可行(行为断言)
- eventPass=false 的可通行图块出现在路径终点时合法、出现在中间节点时被拒绝(行为断言)
- shouldHit=true 的可通行图块出现在路径终点时合法、出现在中间节点时被拒绝(行为断言)
- 未注入谓词(默认)时所有边不可行;注入谓词后邻域方向数与 `DirectionMapper.map(InternalDirectionGroup.Dir4)` 一致为 4行为断言
- 越界/空数据输入触发 logger.warn 新码并得到空结果而非异常(行为断言)
- graph.ts 与 types.ts 中无任何来自 data-state 的引入:对文件运行来源断言找不到 data-state 引用语句(源断言)
- graph.ts 中无任何来自 data-state 的引入:对文件运行来源断言找不到 data-state 引用语句(源断言)
</acceptance_criteria>
<done>有向图构建在 Node 下全绿,边语义与 canPass 判定同源。</done>
<done>授权修正落地且 types.ts 其余零改动;有向图构建在 Node 下全绿,边语义与注入谓词同源。</done>
</task>
<task type="auto">
<name>Task 3: 最小损失搜索 + 仅取路径 API + 回退策略槽位L2 核心)</name>
<files>packages-user/data-system/src/pathfinding/system.ts, packages-user/data-system/src/pathfinding/types.ts, packages-user/data-system/src/pathfinding/index.ts, packages-user/data-system/src/pathfinding/system.test.ts</files>
<read_first>.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md, packages-user/data-system/src/pathfinding/graph.ts, packages-user/data-system/src/event/system.ts, packages-user/data-system/src/combat/mapDamage.ts, packages-user/data-common/src/common/mover.ts, dev.md</read_first>
<action>创建 system.ts`PathfindingSystem` 类,骨架照 GameEventSystem 形态:构造器持 state、可空成员显式类型、`useXxx()` 注入方法per PATTERNS与 index.ts barrel照 event/index.ts 三行形态,并在 data-system/src/index.ts 追加 pathfinding 导出)。实现:(1) 自写 DijkstraO(V²),约 40 行,零依赖)在 Task 2 的有向图上求两点间损失最小路径,满足 D-01/D-02 语义;(2) 损失函数注入槽位,默认每格损失 1D-01损失守卫——函数返回非有限数或负数时 `logger.warn` 新码并按默认损失 1 处理(威胁 T-02-02(3) 仅取路径方法:绑定地图与 `IObjectMovable` 后可调用返回坐标路径数组不调用任何移动方法D-06D-03 绑定槽位);(4) 不可达语义目标不可达且不属于「no-pass 目标 + 可达相邻格」情形时返回空数组、不移动D-08 情形 2情形 1 的相邻格解析留给 02-03本任务先暴露图上「目标四邻中存在可达格」的查询以便 L3 判定——具体 API 形态以拍板草案为准);(5) 回退策略槽位策略函数入参为完整路径及每步到达位置返回是否退回逐步方式D-05移动方式决策——策略判回退则走逐步否则允许瞬移D-04瞬移的实际执行在 02-03本任务只产出决策结果每次寻路动态构建图、不缓存CONTEXT 酌情项默认建议数据端状态可变。system.test.ts 覆盖:默认损失 1 的最短路径、自定义损失改变选路(构造「步数少但损失高」的岔路)、不可达返回空数组、回退策略被以(路径,逐步到达位置序列)调用并生效、未绑定地图/对象时 logger.warn + 空结果mapDamage.ts:169-173 守卫形态)。</action>
<name>Task 3: 最小损失搜索 + 仅取路径 + 回退策略槽位L2 核心,签名照用户 types.ts</name>
<files>packages-user/data-system/src/path/system.ts, packages-user/data-system/src/path/index.ts, packages-user/data-system/src/path/system.test.ts, packages-user/data-system/src/index.ts</files>
<read_first>packages-user/data-system/src/path/types.ts, packages-user/data-system/src/path/graph.ts, packages-user/data-system/src/event/system.ts, packages-user/data-system/src/combat/mapDamage.ts, packages-user/data-common/src/common/mover.ts, dev.md</read_first>
<action>创建 system.ts`PathfindingSystem` 类实现用户 types.ts 的 `IPathfindingSystem`,骨架照 GameEventSystem 形态:构造器持 state、可空成员显式类型、`useXxx()` 注入方法per PATTERNS;公开 `readonly finder: IPathfinder`——finder 实现用户 types.ts 的 `IPathfinder``useMapState(IMapState|null)`、`useMapLayer(IMapLayer|null)`(通常绑事件层)、`useCostFunction(PathCostFunction|null)`、`usePassPredicate(IPassPredicate|null)`、`useDirGroup(group: number)`、`find(start, target): IPathfindingStep[]`)与 index.ts barrel照 event/index.ts 三行形态,并在 data-system/src/index.ts 追加 path 导出)。实现:(1) 自写 DijkstraO(V²),约 40 行,零依赖)在 Task 2 的有向图上求两点间损失最小路径,输出 `IPathfindingStep[]` 步骤序列(每步 `{ dir, from, to }`,非坐标列表),满足 D-01/D-02 语义;(2) 损失函数注入槽位,签名 `PathCostFunction = (block: ILayerLocation) => number`默认每格损失 1D-01损失守卫——函数返回非有限数或负数时 `logger.warn` 新码并按默认损失 1 处理(威胁 T-02-02(3) 仅取路径 `getPath(target): IPathfindingStep[]`:绑定地图与 `IObjectMovable` 后可调用返回步骤序列不调用任何移动方法D-06D-03 绑定槽位 `useMovable(IObjectMovable|null)`(4) 不可达语义:目标不可达时 `find`/`getPath` 返回空数组、不移动D-08 情形 2情形 1 的相邻格可达性由 02-03 直接以 `find(heroPos, 相邻格)` 判定,无需专用查询 API(4) `moveTo`/`teleportTo(target): IPathfindingController | null`:按用户 types.ts 契约实现——绑定 movable 后经其 mover 取得 `IMoverController`,与计算所得路径步骤序列构成 `IPathfindingController { controller, path }` 返回;无法寻路、无路径或已有移动进行中时返回 `null`(授权修正后的 jsdoc 语义D-04移动的实际推进逐步队列驱动/瞬移执行)在 02-03 的 L3 接线完成,本任务产出正确的控制器包装与决策结果;(5) 回退策略槽位 `useFallbackPolicy(policy: PathFallbackPolicy | null)``PathFallbackPolicy = (path: readonly IPathfindingStep[]) => boolean`,返回 true 表示不允许瞬移、必须逐步D-05注入 null 时默认必定回退为逐步寻路per 用户 types.ts jsdoc——「路径上存在事件即回退」的拍板默认策略由 02-03 在 L3 注入移动方式决策——策略判回退则走逐步否则允许瞬移D-04(6) `interrupt(): Promise<void>` 占位实现02-03 Task 4 接管语义);(7) 每次寻路动态构建图、不缓存CONTEXT 酌情项默认建议数据端状态可变。system.test.ts 覆盖:默认损失 1 的最短路径步骤序列、自定义损失改变选路(构造「步数少但损失高」的岔路)、不可达返回空数组、moveTo/teleportTo 无路径或未绑定 movable 时返回 null而非异常、回退策略以路径步骤序列为入参被调用并生效、null 策略默认逐步、未绑定地图/对象时 logger.warn + 空结果mapDamage.ts:169-173 守卫形态)。</action>
<verify>
<automated>pnpm exec vitest run "packages-user/data-system/src/pathfinding/system.test.ts"</automated>
<fails_when>vitest 输出出现 "failed" 字样或非零退出码——最小损失、自定义损失、空数组语义或回退策略断言未通过</fails_when>
<automated>pnpm exec vitest run "packages-user/data-system/src/path/system.test.ts"</automated>
<fails_when>vitest 输出出现 "failed" 字样或非零退出码——最小损失、自定义损失、空数组语义、null 契约或回退策略断言未通过</fails_when>
</verify>
<acceptance_criteria>
- 默认损失下返回格数最少路径;注入自定义损失后选择损失更小的岔路(行为断言)
- 不可达目标返回空数组且移动对象坐标不变(行为断言)
- 回退策略收到路径与每步到达位置并改变决策结果(行为断言)
- 默认损失下返回格数最少的 IPathfindingStep 序列;注入自定义损失后选择损失更小的岔路(行为断言)
- 不可达目标 getPath 返回空数组且移动对象坐标不变(行为断言)
- moveTo/teleportTo 在无路径/未绑定 movable 时返回 null有路径时返回含 controller 与 path 的 IPathfindingController行为断言
- PathFallbackPolicy 收到步骤序列并改变决策结果;注入 null 时决策为逐步(行为断言)
- 未绑定依赖时 warn 码输出且返回空结果不抛异常(行为断言)
- `pnpm exec vitest run "packages-user/data-system/src/pathfinding"` 全目录绿CLI 断言)
- `pnpm exec vitest run "packages-user/data-system/src/path"` 全目录绿CLI 断言)
</acceptance_criteria>
<done>L2 寻路核心在 Node 下全绿:图构建、最小损失、仅取路径、空数组语义、回退决策齐备。</done>
<done>L2 寻路核心在 Node 下全绿:图构建、最小损失、仅取路径步骤序列、空数组语义、控制器包装契约、回退决策齐备,签名与用户 types.ts 逐字一致</done>
</task>
</tasks>
@ -146,9 +158,9 @@ Output: 修复后的 mover.ts + 绿色 L0 回归pathfinding/{types,graph,syst
<threat_model>
## Artifacts this phase produces
- 接口/类型(命名以拍板草案为唯一事实源):损失函数类型(默认每格 1、回退策略函数类型入参路径 + 每步到达位置)、通行性谓词函数类型、图节点/邻接 interface`packages-user/data-system/src/pathfinding/types.ts`
- 类:`PathfindingSystem`L2绑定 `IMapLayer`/`IObjectMovable` 注入槽位D-03
- 函数/方法有向图构建graph.ts)、自写 Dijkstra 最小损失搜索、仅取路径 APID-06、移动方式决策D-04/D-05 槽位、方向→PassBit 映射(谓词默认实现内
- 接口/类型:用户已提交的 `packages-user/data-system/src/path/types.ts``IPathfinder`/`IPathfindingSystem`/`IPathfindingStep`/`IPathfindingController`/`PathCostFunction`/`PathFallbackPolicy`commit 7a011b2——本计划仅消费唯一改动为用户授权的 moveTo/teleportTo 返回类型追加 `| null`
- 类:`PathfindingSystem` + `IPathfinder` 实现L2绑定 `useMapState`/`useMapLayer`/`useMovable` 与 `usePassPredicate`/`useCostFunction`/`useDirGroup` 注入槽位D-03
- 函数/方法有向图构建graph.ts`IDirectionMapper.map` 邻域解析)、自写 Dijkstra 最小损失搜索、`getPath` 仅取路径步骤序列D-06、`IPathfindingController` 包装契约、移动方式决策D-04/D-05 槽位
- 修改:`packages-user/data-common/src/common/mover.ts`mover.ts:651 回写条件,按拍板)、`packages/common/src/logger.json`(新 warn 码 ≥173、`packages-user/data-system/src/index.ts`barrel 追加)
- 测试:`mover.test.ts`(翻绿)、`graph.test.ts`、`system.test.ts`
@ -171,13 +183,13 @@ Output: 修复后的 mover.ts + 绿色 L0 回归pathfinding/{types,graph,syst
<verification>
- `pnpm exec vitest run "packages-user/data-common/src/common/mover.test.ts"` 4/4 passed
- `pnpm exec vitest run "packages-user/data-system/src/pathfinding"` 全绿
- `pnpm check:type 2>&1 | Select-String -Pattern "pathfinding|mover\.ts"` 无输出Phase 01 同款过滤式类型门,仓库既有诊断不在本计划范围)
- `pnpm check:circular` 输出中不含 pathfinding 相关路径eventStore 既有循环为 Phase 01 基线,维持不变)
- `pnpm exec vitest run "packages-user/data-system/src/path"` 全绿
- `pnpm check:type 2>&1 | Select-String -Pattern "src[\\/]path[\\/]|mover\.ts"` 无输出Phase 01 同款过滤式类型门,仓库既有诊断不在本计划范围)
- `pnpm check:circular` 输出中不含 path 相关路径eventStore 既有循环为 Phase 01 基线,维持不变)
</verification>
<success_criteria>
L0 缺陷按拍板修复且有回归护栏L2 寻路核心全部行为(有向边、单向门、终端节点、最小损失、自定义损失、仅取路径、空数组、回退决策)在 Node 下测试验证通过。
L0 缺陷按拍板修复且有回归护栏;path/types.ts 仅含授权的返回类型修正;L2 寻路核心全部行为(有向边、单向门、终端节点、useDirGroup/Dir4 邻域、最小损失、自定义损失、仅取路径步骤序列、空数组、控制器包装契约、回退决策)在 Node 下测试验证通过,签名与用户 types.ts 逐字一致
</success_criteria>
<output>

View File

@ -5,34 +5,40 @@ type: execute
wave: 3
depends_on: ["02-01", "02-02"]
files_modified:
- packages-user/data-state/src/pathfinding/heroPathfinding.ts
- packages-user/data-state/src/pathfinding/heroPathfinding.test.ts
- packages-user/data-state/src/path/heroPathfinding.ts
- packages-user/data-state/src/path/heroPathfinding.test.ts
- packages-user/data-state/src/hero/moverImpl.ts
- packages-user/data-base/src/hero/mover.ts
- packages-user/data-state/src/core.ts
- packages-user/data-state/src/index.ts
- packages/common/src/logger.json
autonomous: true
requirements: [PATH-01, PATH-02]
estimate:
tokens: 75000
raw_tokens: 75000
tasks: 3
tokens: 85000
raw_tokens: 85000
tasks: 4
confidence: low
must_haves:
truths:
- "逐步自动寻路端到端可用:路径翻译为 Dir 步骤队列并复用 `DefaultHeroMoveTopImpl` 执行,每步走 enter/leave/hit hooks途经事件自然触发 (D-09)"
- "瞬移执行可用:决策允许瞬移时经 `ObjectMoveType.Teleport` 到达目标;拍板的回退策略判定需回退时自动退为逐步寻路,默认策略为路径上存在事件即回退 (D-04, D-05)"
- "DefaultHeroMoveTopImpl 通行性判断已重构为 IPassPredicatepredicate() 返回谓词实现hero mover onStepStart 经 predicate().canPass/shouldHitIPassCheckHandler: currLoc/nextLoc/direction/floorId判定Phase 1 enter/leave/hit 事件派发行为不变D-07, D-08, D-09"
- "逐步自动寻路端到端可用hero mover 的 predicate() 经 usePassPredicate 注入 L2`find` 取得的 IPathfindingStep 步骤序列翻译为 Dir 步骤队列并复用 `DefaultHeroMoveTopImpl` 执行,每步走 enter/leave/hit hooks途经事件自然触发 (D-09)"
- "瞬移执行可用:决策允许瞬移时经 `ObjectMoveType.Teleport` 到达目标;`PathFallbackPolicy(path) => boolean` 判定需回退true = 必须逐步)时自动退为逐步寻路,拍板默认策略「路径上存在事件即回退」在 L3 注入 (D-04, D-05)"
- "D-08 情形 1no-pass 目标存在可达相邻格时,移动到该相邻格、按拍板方案派发目标 OnTouch、勇士面朝目标 (D-08)"
- "自动寻路途中被新寻路调用或显式打断入口打断后,新寻路从最新位置接管 (D-10)"
- "自动寻路途中被新寻路调用或显式打断入口interrupt()打断后,新寻路从最新位置接管 (D-10)"
- "数据端移动入口已在 data-state barrel 导出Node 冒烟测试可调用;渲染端点击接线不在本阶段 (D-11)"
- "CoreState 构造器完成寻路系统接线(照 useTopImplementation 同处接线模式)(D-09)"
artifacts:
- "packages-user/data-state/src/pathfinding/heroPathfinding.ts"
- "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts"
- "packages-user/data-state/src/path/heroPathfinding.ts"
- "packages-user/data-state/src/path/heroPathfinding.test.ts"
- "packages-user/data-state/src/hero/moverImpl.ts"
- "packages-user/data-base/src/hero/mover.ts"
- "packages-user/data-state/src/core.ts"
- "packages-user/data-state/src/index.ts"
- "packages/common/src/logger.json"
key_links:
- "heroPathfinding.ts 注入 02-02 的损失/回退/谓词槽位并绑定 hero moverL3 → L2 合法方向,反向即循环依赖)"
- "heroPathfinding.ts 将 state.hero mover 的 predicate()moverImpl 重构产物)注入 02-02 的 usePassPredicate并注入损失/回退槽位、绑定 hero moverL3 → L2 合法方向,反向即循环依赖)"
- "data-base/src/hero/mover.ts 的 onStepStart 判定改经 topImpl.predicate().canPass/shouldHitIPassCheckHandler 构造),与 moverImpl 重构为同一接口契约的两端"
- "D-08 OnTouch 派发走 Phase 1 source-aware 执行链executor.executeenv 形态复刻 moverImpl commonTrigger"
- "打断接管经 `IMoverController.stop()`先停旧再起新mover.start 移动中返回 null"
assumptions:
@ -42,13 +48,14 @@ must_haves:
- "不得为寻路引入任何新 npm 依赖(零依赖约束)"
- "不得把渲染端点击处理接进数据端代码 (D-11)"
- "D-08 OnTouch 派发必须按用户拍板方案实现,不得静默替换 (D-07, D-08)"
- "path/types.ts用户已提交不得重写moverImpl 重构仅提取 predicate()Phase 1 事件派发语义不得变更 (D-07, D-09)"
---
<objective>
在 data-stateL3完成寻路系统的移动执行接线逐步寻路复用 hero mover 与 Phase 1 事件链、瞬移与默认回退策略、D-08 情形 1 相邻格 + OnTouch + 面朝目标、打断接管,以及 CoreState/barrel 装配、logger 码登记与阶段门禁。
在 data-stateL3完成寻路系统的移动执行接线先把 moverImpl 通行性判断重构为用户新接口要求的 `IPassPredicate``predicate()`),再落地逐步寻路复用 hero mover 与 Phase 1 事件链、瞬移与拍板默认回退策略、D-08 情形 1 相邻格 + OnTouch + 面朝目标、打断接管,以及 CoreState/barrel 装配、logger 码登记与阶段门禁。接口签名以用户已提交的 `packages-user/data-system/src/path/types.ts`commit 7a011b2为唯一事实源。
Purpose: 让寻路从「算得出」变成「走得通」——勇士真实移动、事件真实触发、输入真实接管;这是 PATH-01 的最终形态与 PATH-02 的数据端入口。
Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、logger.json全阶段门禁通过。
Output: moverImpl predicate 重构 + heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、logger.json全阶段门禁通过。
</objective>
<execution_context>
@ -65,9 +72,10 @@ Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、log
@.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md
@.planning/phases/02-pathfinding/02-02-SUMMARY.md
# L2 寻路核心与 L3 既有接线点
@packages-user/data-system/src/pathfinding/system.ts
@packages-user/data-system/src/pathfinding/types.ts
# L2 寻路核心(用户 types.ts 为接口事实源)与 L3 既有接线点
@packages-user/data-system/src/path/system.ts
@packages-user/data-system/src/path/types.ts
@packages-user/data-base/src/map/types.ts
@packages-user/data-state/src/hero/moverImpl.ts
@packages-user/data-state/src/core.ts
@packages-user/data-state/src/index.ts
@ -81,36 +89,55 @@ Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、log
<tasks>
<task type="tracer">
<name>Task 1: 逐步自动寻路端到端接线CoreState + 路径翻译 + hero mover 执行)</name>
<files>packages-user/data-state/src/pathfinding/heroPathfinding.ts, packages-user/data-state/src/pathfinding/heroPathfinding.test.ts, packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts</files>
<read_first>.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-state/src/core.ts, packages-user/data-base/src/hero/mover.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-system/src/event/eventDispatch.test.ts, dev.md</read_first>
<action>创建 L3 `HeroPathfinding`imports 骨架照 moverImpl.ts:1-32`@user/data-system` 引入 L2 寻路系统——此方向合法)。职责:构造时绑定 `IObjectMovable`hero mover`IMapLayer`event 层)到 02-02 的 `PathfindingSystem` 注入槽位D-03注入默认通行性谓词复刻 `DefaultHeroMoveTopImpl` canPass 语义)与回退策略槽位。实现逐步执行:调用寻路系统取得最小损失路径后,将路径翻译为 `ObjectMoveType.Dir` 步骤队列(相邻坐标增量 → `FaceDirection`,每次一步保证 canPass 逐格判定per RESEARCH Code Examples 语义),经 mover `push` + `start` 启动D-09。core.ts 在 `//#endregion 勇士顶层初始化` region 内、`useTopImplementation` 之后新增寻路接线(照 core.ts:235-237 模式data-state/src/index.ts 在 `export * from './hero';` 之后追加寻路导出dev.md不转发导出。data-system/src/index.ts 的 pathfinding 导出已由 02-02 Task 3 落地(该文件的唯一归属任务),本任务只验证其存在且可被 `@user/data-system` 引入不重复写入该行。heroPathfinding.test.ts 用 eventDispatch.test.ts:105-179 fixture 模式:勇士在 fixture 地图上自动寻路走两步断言终点坐标、leave/enter 事件按 Phase 1 source-aware 顺序触发、途经带事件的图块事件被执行。签名与命名以拍板草案为唯一事实源D-07)。</action>
<task type="auto">
<name>Task 1: moverImpl 通行性判断重构为 IPassPredicatepredicate()+ hero mover 调用点改接</name>
<files>packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-base/src/hero/mover.ts</files>
<read_first>packages-user/data-base/src/hero/types.ts, packages-user/data-base/src/map/types.ts, packages-user/data-base/src/hero/mover.ts, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-state/src/coreEventLayer.test.ts, packages-user/data-common/src/common/mover.ts, dev.md</read_first>
<action>用户的接口重构commit 7a011b2`IHeroMoveTopImpl``canPass(handler)/shouldHit(handler)` 移除、新增 `predicate(): IPassPredicate``@user/data-base` map/types.ts §通行检测:`IPassPredicate.canPass/shouldHit(IPassCheckHandler)`handler 为 `{ currLoc, nextLoc, direction, floorId }`),现有 moverImpl.ts 不再满足接口。本任务做行为保持型重构:(1) 在 `DefaultHeroMoveTopImpl` 内把现 `canPass`moverImpl.ts:63-125含四角朝向直通、事件层掩码、其余层 onlyEvents 掩码)与 `shouldHit`moverImpl.ts:127-141eventPass 判定)的判定逻辑提取为一个 `IPassPredicate` 实现类;谓词不再依赖 `IHeroMoveTopHandler.face``IPassCheckHandler` 无 face 字段)——反向掩码位由 `FaceDirection` 的反向映射直接得出(语义与 moverImpl.ts:85-87 经 face.opposite 等价);实现 `predicate(): IPassPredicate` 返回该实例;类内原 `canPass/shouldHit` 公有方法移除。(2) 改接 `packages-user/data-base/src/hero/mover.ts` 的调用点:`onStepStart` 内 mover.ts:183 `this.topImpl.canPass(handler)` 与 mover.ts:185 `this.topImpl.shouldHit(handler)` 改为从 `this.topImpl.predicate()` 取谓词,并以 `IPassCheckHandler` 形态currLoc/nextLoc/direction/floorId从现有 handler 字段提取)调用 `canPass/shouldHit`HeroMoveCode 分支结构Step/Hit/CannotMove不变。(3) 保持 Phase 1 事件派发行为完整不变enter/leave/hit → OnEnter/OnLeave/OnTouchD-08 语义 per 拍板记录——hit/OnTouch 仅在掩码允许到达且目标 no-pass 时触发,掩码导致的不可达走 CannotMove 永不触发 OnTouch。D-08 拍板语义映射:`canPass=true` 且 `shouldHit=true` → HeroMoveCode.Hit → OnTouch`canPass=false` → CannotMove不触发。全部 jsDoc 中文注释、禁 `as`、禁 `import type`dev.md。回归护栏coreEventLayer.test.ts 与全量既有测试必须全绿(行为保持证明)。</action>
<verify>
<automated>pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts"</automated>
<automated>pnpm exec vitest run "packages-user/data-state/src/coreEventLayer.test.ts"; if ($?) { pnpm exec vitest run "packages-user/data-base/src" }</automated>
<fails_when>任一 vitest 输出出现 "failed" 字样或非零退出码——moverImpl 重构改变了 Phase 1 事件派发或 hero 移动判定行为</fails_when>
</verify>
<acceptance_criteria>
- DefaultHeroMoveTopImpl 满足新 IHeroMoveTopImpl含 predicate(): IPassPredicate无 canPass/shouldHit 公有方法(源断言)
- data-base/src/hero/mover.ts 中无 `topImpl.canPass(`/`topImpl.shouldHit(` 直接调用,改经 predicate()(源断言)
- coreEventLayer.test.ts 与 data-base 既有测试全绿Phase 1 OnEnter/OnLeave/OnTouch 行为不变(行为断言)
- 掩码语义保持单向门outPass/inPass 方向位)、事件层恒参与、其余层仅 onlyEvents 参与的行为与重构前等价(行为断言,由既有测试覆盖)
</acceptance_criteria>
<done>moverImpl 满足用户新接口且行为保持predicate() 可被 L3 注入 usePassPredicatehero 移动判定与事件派发零回归。</done>
</task>
<task type="tracer">
<name>Task 2: 逐步自动寻路端到端接线CoreState + usePassPredicate 注入 + 路径翻译 + hero mover 执行)</name>
<files>packages-user/data-state/src/path/heroPathfinding.ts, packages-user/data-state/src/path/heroPathfinding.test.ts, packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts</files>
<read_first>packages-user/data-system/src/path/types.ts, packages-user/data-system/src/path/system.ts, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-state/src/core.ts, packages-user/data-base/src/hero/mover.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-system/src/event/eventDispatch.test.ts, dev.md</read_first>
<action>创建 L3 `HeroPathfinding` 类于 `packages-user/data-state/src/path/heroPathfinding.ts`imports 骨架照 moverImpl.ts:1-32`@user/data-system` 引入 L2 寻路系统——此方向合法)。职责:构造时绑定 `IObjectMovable`hero mover到 02-02 `PathfindingSystem``useMovable` 槽位、事件层 `IMapLayer` 到 finder 的 `useMapLayer` 槽位D-03将 Task 1 重构所得 hero mover 顶层实现的 `predicate()``usePassPredicate` 注入 L2D-03/D-09 关键接线注入拍板默认回退策略「路径上存在事件即回退」Task 3 实现细节,本任务先接策略槽位)。实现逐步执行:调用 `finder.find`/`system.getPath` 取得 `IPathfindingStep[]` 最小损失步骤序列后,将步骤的 `dir``FaceDirection`)翻译为 `ObjectMoveType.Dir` 步骤队列(每步一步,保证 canPass 逐格判定per RESEARCH Code Examples 语义),经 mover `push` + `start` 启动D-09heroPathfinding 的公开入口与 `IPathfindingSystem``moveTo/teleportTo/getPath/interrupt` 对齐(签名以用户 types.ts 为唯一事实源D-07。core.ts 在 `//#endregion 勇士顶层初始化` region 内、`useTopImplementation` 之后新增寻路接线(照 core.ts:235-237 模式data-state/src/index.ts 在 `export * from './hero';` 之后追加 path 接线导出dev.md不转发导出。data-system/src/index.ts 的 path 导出已由 02-02 Task 3 落地(该文件的唯一归属任务),本任务只验证其存在且可被 `@user/data-system` 引入不重复写入该行。heroPathfinding.test.ts 用 eventDispatch.test.ts:105-179 fixture 模式:勇士在 fixture 地图上自动寻路走两步断言终点坐标、leave/enter 事件按 Phase 1 source-aware 顺序触发、途经带事件的图块事件被执行。</action>
<verify>
<automated>pnpm exec vitest run "packages-user/data-state/src/path/heroPathfinding.test.ts"</automated>
<fails_when>vitest 输出出现 "failed" 字样或非零退出码——勇士未到达目标、事件未按序触发或翻译步骤数与路径不符</fails_when>
</verify>
<acceptance_criteria>
- 勇士沿最小损失路径逐步移动并停在目标格,坐标断言通过(行为断言)
- 途经图块的 leave/enter 钩子触发且 env 的 heroLocator 与实际位置一致(行为断言)
- hero 的 predicate() 已注入 L2 usePassPredicateL2 未注入谓词时不可行,注入后图可达——行为断言)
- core.ts 含寻路接线且位于勇士顶层初始化 region源断言
- 两个 barrel 均导出寻路内容,`pnpm check:type 2>&1 | Select-String -Pattern "heroPathfinding|pathfinding|core\.ts"` 无输出CLI 断言)
- 两个 barrel 均导出寻路内容,`pnpm check:type 2>&1 | Select-String -Pattern "heroPathfinding|src[\\/]path[\\/]|core\.ts"` 无输出CLI 断言)
</acceptance_criteria>
<done>勇士能在 fixture 地图上自动寻路移动到指定格途经事件自然触发——PATH-01 的核心闭环成立。</done>
</task>
<task type="auto">
<name>Task 2: 瞬移执行 + 默认回退策略 + D-08 情形 1相邻格 + OnTouch + 面朝)</name>
<files>packages-user/data-state/src/pathfinding/heroPathfinding.ts, packages-user/data-state/src/pathfinding/heroPathfinding.test.ts</files>
<read_first>.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md, packages-user/data-state/src/pathfinding/heroPathfinding.ts, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-common/src/event/types.ts, packages-user/data-base/src/map/types.ts, .planning/phases/02-pathfinding/02-RESEARCH.md</read_first>
<action>扩展 heroPathfinding.ts(1) 瞬移执行——移动方式决策允许瞬移时以 `ObjectMoveType.Teleport` 步骤到达目标D-04(2) 默认回退策略——按拍板草案实现「路径上存在事件即回退」:沿路径用 moverImpl.ts:169-204 同款方式探测事件(点事件 + 静态/动态图块事件命中即退为逐步执行D-05策略判不需回退时才瞬移(3) D-08 情形 1——目标为 no-pass 图块且四邻存在可达格时:寻路至该相邻格并移动到达,按用户拍板方案派发目标 OnTouch拍板方案 A直接构造 `IGameEventInvocation`env 逐字复刻 moverImpl.ts:206-222 `commonTrigger` 形态——state、type 按目标事件来源、trigger=`EventTrigger.OnTouch`、heroLocator=相邻格、triggerLocator=目标格、tile/layer/map 按 env 接口可空语义填实,调 `executor.execute`;拍板方案 B按用户拍板的替代语义实现随后以纯转向步或 `setFaceDir` 使勇士面朝目标D-08。测试补充瞬移在无事件路径上直达目标路径含事件时默认策略退为逐步且途经事件触发no-pass 目标带 OnTouch 事件时勇士停于相邻格、面朝目标、mock 事件以 OnTouch 触发且 env.triggerLocator 为目标格mock 事件照 eventDispatch.test.ts:181-211 addEvent 模式无相邻可达格的不可达目标保持空数组不移动D-08 情形 2 回归)。</action>
<name>Task 3: 瞬移执行 + 拍板默认回退策略 + D-08 情形 1相邻格 + OnTouch + 面朝)</name>
<files>packages-user/data-state/src/path/heroPathfinding.ts, packages-user/data-state/src/path/heroPathfinding.test.ts</files>
<read_first>packages-user/data-system/src/path/types.ts, packages-user/data-state/src/path/heroPathfinding.ts, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-common/src/event/types.ts, packages-user/data-base/src/map/types.ts, .planning/phases/02-pathfinding/02-RESEARCH.md</read_first>
<action>扩展 heroPathfinding.ts(1) 瞬移执行——`PathFallbackPolicy(path) => boolean`path 为 `IPathfindingStep[]` 步骤序列)决策允许瞬移(返回 false时以 `ObjectMoveType.Teleport` 步骤到达目标D-04(2) 拍板默认回退策略——「路径上存在事件即回退」:沿步骤序列逐格用 moverImpl.ts:169-204 同款方式探测事件(点事件 + 静态/动态图块事件),命中即策略返回 true必须逐步、禁止瞬移L3 将此策略经 `useFallbackPolicy` 注入 L2D-05L2 的 null 默认为必定逐步本策略为游戏默认per 用户 types.ts + 拍板)(3) D-08 情形 1——目标为 no-pass 图块且四邻存在可达格时(相邻格可达性以 `finder.find(heroPos, 相邻格)` 判定):寻路至该相邻格并移动到达,按用户拍板方案派发目标 OnTouch拍板方案 A直接构造 `IGameEventInvocation`env 逐字复刻 moverImpl.ts:206-222 `commonTrigger` 形态——state、type 按目标事件来源、trigger=`EventTrigger.OnTouch`、heroLocator=相邻格、triggerLocator=目标格、tile/layer/map 按 env 接口可空语义填实,调 `executor.execute`;拍板方案 B按用户拍板的替代语义实现随后以纯转向步或 `setFaceDir` 使勇士面朝目标D-08。测试补充瞬移在无事件路径上直达目标路径含事件时默认策略退为逐步且途经事件触发no-pass 目标带 OnTouch 事件时勇士停于相邻格、面朝目标、mock 事件以 OnTouch 触发且 env.triggerLocator 为目标格mock 事件照 eventDispatch.test.ts:181-211 addEvent 模式无相邻可达格的不可达目标保持空数组不移动D-08 情形 2 回归)。</action>
<verify>
<automated>pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts"</automated>
<automated>pnpm exec vitest run "packages-user/data-state/src/path/heroPathfinding.test.ts"</automated>
<fails_when>vitest 输出出现 "failed" 字样或非零退出码——瞬移未直达、事件路径未回退逐步、OnTouch 未按拍板方案触发或面朝方向错误</fails_when>
</verify>
<acceptance_criteria>
- 无事件路径瞬移一步到达目标,坐标断言通过(行为断言)
- 含事件路径在默认策略下退为逐步且途经事件触发(行为断言)
- 含事件路径在默认策略(返回 true下退为逐步且途经事件触发(行为断言)
- no-pass 目标勇士位于相邻格、朝向指向目标、OnTouch 事件以拍板方案派发且 triggerLocator 为目标格(行为断言)
- 四邻皆不可达的 no-pass 目标:路径空数组、勇士不动(行为断言)
</acceptance_criteria>
@ -118,21 +145,21 @@ Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、log
</task>
<task type="auto">
<name>Task 3: 打断接管 + logger 码登记 + 阶段门禁</name>
<files>packages-user/data-state/src/pathfinding/heroPathfinding.ts, packages-user/data-state/src/pathfinding/heroPathfinding.test.ts, packages/common/src/logger.json</files>
<read_first>.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md, packages-user/data-state/src/pathfinding/heroPathfinding.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-base/src/hero/mover.ts, packages/common/src/logger.json, .planning/phases/02-pathfinding/02-RESEARCH.md</read_first>
<action>实现打断接管D-10寻路控制器持有当前 `IMoverController` 引用新寻路调用或显式打断入口到达时按拍板的时序stop 后 await 兑现,或 onEnd 回调驱动)停止旧移动,从最新坐标起算并启动新路径——注意 mover.start 移动中返回 nullmover.ts:669-670必须先完成旧移动的停止再 start识别 HeroMover.onStepEnd 在 CannotMove/Stop/Hit 时的自然终止,避免对已终止移动重复 stopRESEARCH Pitfall 5。在 logger.json 集中登记本阶段全部新码warn ≥173、error ≥65与 02-02 已登记码合并查重RESEARCH Pitfall 7。测试补充移动中途发起新寻路旧路径中止且新路径从最新位置起算到达新目标显式打断入口等效。最后跑阶段门禁并修复本阶段文件暴露的问题。</action>
<name>Task 4: 打断接管 + logger 码登记 + 阶段门禁</name>
<files>packages-user/data-state/src/path/heroPathfinding.ts, packages-user/data-state/src/path/heroPathfinding.test.ts, packages/common/src/logger.json</files>
<read_first>packages-user/data-system/src/path/types.ts, packages-user/data-state/src/path/heroPathfinding.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-base/src/hero/mover.ts, packages/common/src/logger.json, .planning/phases/02-pathfinding/02-RESEARCH.md</read_first>
<action>实现打断接管D-10寻路控制器持有当前 `IMoverController` 引用(即 02-02 返回的 `IPathfindingController.controller`L2 `interrupt(): Promise<void>` 的语义在此接线——新寻路调用或显式 `interrupt()` 入口到达时,按拍板时序选项 1stop 后 await 兑现,再起新寻路)停止旧移动,从最新坐标起算并启动新路径——注意 mover.start 移动中返回 nullmover.ts:669-670必须先完成旧移动的停止await 兑现)再 start识别 HeroMover.onStepEnd 在 CannotMove/Stop/Hit 时的自然终止,避免对已终止移动重复 stopRESEARCH Pitfall 5。在 logger.json 集中登记本阶段全部新码warn ≥173、error ≥65与 02-02 已登记码合并查重RESEARCH Pitfall 7。测试补充移动中途发起新寻路旧路径中止且新路径从最新位置起算到达新目标显式 interrupt() 入口等效。最后跑阶段门禁并修复本阶段文件暴露的问题。</action>
<verify>
<automated>pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts"; if ($?) { pnpm check:type 2>&1 | Select-String -Pattern "pathfinding|mover\.ts|core\.ts" }</automated>
<automated>pnpm exec vitest run "packages-user/data-state/src/path/heroPathfinding.test.ts"; if ($?) { pnpm check:type 2>&1 | Select-String -Pattern "src[\\/]path[\\/]|mover\.ts|core\.ts" }</automated>
<fails_when>vitest 输出出现 "failed" 字样,或 Select-String 对本阶段文件输出任何类型诊断行(命中即存在计划内文件的类型错误)</fails_when>
</verify>
<acceptance_criteria>
- 移动中发起新寻路:旧路径停止、新路径自最新坐标起算并到达新目标(行为断言)
- 显式打断后 mover 处于可再次 start 的干净状态(行为断言)
- logger.json 新码无撞码warn 新码 ≥173、error 新码 ≥65 且与既有键位不重复CLI 断言node 枚举 logger.json 键位核对)
- `pnpm check:circular` 输出不含 pathfinding 相关路径;`pnpm lint:user` 对本阶段文件无报错CLI 断言)
- `pnpm check:circular` 输出不含 path 相关路径;`pnpm lint:user` 对本阶段文件无报错CLI 断言)
</acceptance_criteria>
<done>打断接管按拍板时序可用;全量测试绿、循环依赖与 lint 门禁对本阶段文件清洁。</done>
<done>打断接管按拍板时序stop 兑现后起新)可用;全量测试绿、循环依赖与 lint 门禁对本阶段文件清洁。</done>
</task>
</tasks>
@ -140,10 +167,11 @@ Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、log
<threat_model>
## Artifacts this phase produces
- 类:`HeroPathfinding`L3 接线类,`packages-user/data-state/src/pathfinding/heroPathfinding.ts`
- 方法:路径→`ObjectMoveType.Dir` 步骤翻译、逐步执行(复用 `DefaultHeroMoveTopImpl` 事件链D-09、瞬移执行D-04、默认回退策略「路径上存在事件即回退」D-05、D-08 情形 1 相邻格 + OnTouch + 面朝、打断接管入口D-10
- 修改:`packages-user/data-state/src/core.ts`CoreState 寻路接线)、`packages-user/data-state/src/index.ts`barrel、`packages/common/src/logger.json`(集中登记码位);`packages-user/data-system/src/index.ts` 的 pathfinding 导出已由 02-02 Task 3 落地(该文件唯一归属在 02-02本计划仅验证不修改
- 测试:`heroPathfinding.test.ts`(端到端逐步/瞬移/回退/D-08 双语义/打断)
- 类:`HeroPathfinding`L3 接线类,`packages-user/data-state/src/path/heroPathfinding.ts`
- 类/方法(重构):`DefaultHeroMoveTopImpl` 的 `predicate(): IPassPredicate`(通行性掩码逻辑提取为 `IPassPredicate` 实现,行为保持);`data-base/src/hero/mover.ts` onStepStart 改经 predicate().canPass/shouldHit
- 方法:路径步骤序列→`ObjectMoveType.Dir` 步骤翻译、逐步执行(复用 hero mover 事件链D-09、瞬移执行D-04、拍板默认回退策略「路径上存在事件即回退」经 useFallbackPolicy 注入D-05、D-08 情形 1 相邻格 + OnTouch + 面朝、打断接管入口D-10
- 修改:`packages-user/data-state/src/core.ts`CoreState 寻路接线)、`packages-user/data-state/src/index.ts`barrel、`packages/common/src/logger.json`(集中登记码位);`packages-user/data-system/src/index.ts` 的 path 导出已由 02-02 Task 3 落地(该文件唯一归属在 02-02本计划仅验证不修改
- 测试:`heroPathfinding.test.ts`(端到端逐步/瞬移/回退/D-08 双语义/打断moverImpl 重构以既有 coreEventLayer.test.ts 与 data-base 测试为回归护栏
## Trust Boundaries
@ -162,15 +190,16 @@ Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、log
</threat_model>
<verification>
- `pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts"` 全绿
- `pnpm exec vitest run "packages-user/data-state/src/path/heroPathfinding.test.ts"` 全绿
- `pnpm exec vitest run "packages-user/data-state/src/coreEventLayer.test.ts"` 全绿moverImpl 重构回归护栏)
- `pnpm exec vitest run` 全量通过Phase 02 + Phase 01 既有测试无回归)
- `pnpm check:type 2>&1 | Select-String -Pattern "pathfinding|mover\.ts|core\.ts"` 无输出
- `pnpm check:circular` 输出不含 pathfinding 相关路径eventStore 既有循环维持 Phase 01 基线)
- `pnpm check:type 2>&1 | Select-String -Pattern "src[\\/]path[\\/]|mover\.ts|core\.ts"` 无输出
- `pnpm check:circular` 输出不含 path 相关路径eventStore 既有循环维持 Phase 01 基线)
- `pnpm lint:user` 无新报错
</verification>
<success_criteria>
勇士在 fixture 地图上可自动寻路(逐步/瞬移/回退/不可达双语义/打断接管全部符合拍板契约);数据端入口经 barrel 导出且 Node 可验证阶段门禁全绿。PATH-01 交付完成PATH-02 以数据端入口形态交付(点击接线属 Phase 4per D-11
moverImpl 满足用户重构后的 IHeroMoveTopImplpredicate(): IPassPredicate且 Phase 1 行为零回归;勇士在 fixture 地图上可自动寻路(逐步/瞬移/回退/不可达双语义/打断接管全部符合拍板契约);数据端入口经 barrel 导出且 Node 可验证阶段门禁全绿。PATH-01 交付完成PATH-02 以数据端入口形态交付(点击接线属 Phase 4per D-11
</success_criteria>
<output>