| phase |
plan |
type |
wave |
depends_on |
files_modified |
autonomous |
requirements |
estimate |
must_haves |
| 02-pathfinding |
02 |
execute |
2 |
|
| packages-user/data-common/src/common/mover.ts |
| packages-user/data-common/src/common/mover.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 |
|
true |
|
| tokens |
raw_tokens |
tasks |
confidence |
| 70000 |
70000 |
3 |
low |
|
| truths |
artifacts |
key_links |
assumptions |
prohibitions |
| mover.ts:651 坐标回写条件按用户拍板结果修复,L0 回归 4 用例(x 正交/y 正交/斜向/传送)全部翻绿 (D-07) |
| path/types.ts 的 moveTo/teleportTo 返回类型已按用户授权修正为 `IPathfindingController | null`,除此之外该文件零改动(用户提交 7a011b2,user-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) |
| 回退策略槽位存在:`PathFallbackPolicy(path) => boolean`(true = 必须逐步、禁止瞬移),注入 null 时默认必定回退为逐步寻路;「路径上存在事件即回退」的拍板默认策略由 02-03 在 L3 注入 (D-04, D-05) |
| 绑定面为 `useMapState`/`useMapLayer`/`useMovable` 注入槽位与 `usePassPredicate` 槽位(默认均不可通行),L2 未引用 L3 任何符号 (D-03) |
|
| packages-user/data-common/src/common/mover.ts |
| packages-user/data-common/src/common/mover.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 |
|
| graph.ts 消费注入的 IPassPredicate(canPass 判边可行性、shouldHit 分类终端/撞击节点)而非 import L3;掩码语义谓词由 02-03 Task 1 从 moverImpl 重构为 predicate() 后经 usePassPredicate 注入 |
| system.ts 的损失/回退/谓词槽位由 02-03 的 L3 接线填充(hero mover 的 predicate() 注入 usePassPredicate) |
|
| PATH-01(flagged assumption,spec-less probe 未分类):本计划交付自动寻路的纯计算核心(图构建 + 最小损失搜索 + 仅取路径),Node 可验证 |
| PATH-02(flagged assumption,spec-less probe 未分类):本计划提供的仅取路径 API 即渲染端 Phase 4 将调用的数据端入口之一,点击接线不在本阶段 (D-11) |
|
| 不得为寻路引入任何新 npm 依赖(Dijkstra 自写,零新包) |
| 不得把渲染端点击处理接进数据端代码 (D-11) |
| path/types.ts 为用户所有(commit 7a011b2),除用户授权的 moveTo/teleportTo 返回类型修正外不得重写、更名或改层 (D-07) |
|
|
按用户拍板结果修复 L0 mover.ts:651 坐标回写缺陷并翻绿回归测试,然后在 data-system
(L2
)落地寻路核心
:有向图构建
(注入 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 回归;path/{graph,system,index}.ts 及 co-located 测试;path/types.ts 的授权返回类型修正。
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/02-pathfinding/02-CONTEXT.md
@.planning/phases/02-pathfinding/02-RESEARCH.md
@.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
@packages-user/data-base/src/map/mapLayer.ts
@packages-user/data-state/src/hero/moverImpl.ts
@packages-user/data-system/src/event/system.ts
@packages-user/data-system/src/combat/mapDamage.ts
@dev.md
Task 1: 按拍板结果修复 L0 坐标回写缺陷并翻绿回归测试
packages-user/data-common/src/common/mover.ts, packages-user/data-common/src/common/mover.test.ts
.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md, packages-user/data-common/src/common/mover.ts, packages-user/data-common/src/common/mover.test.ts
依据 02-01 拍板记录执行。若 P1 获 go
:将 mover.ts `moveProgress` 内 651 行的回写条件按拍板语义修改(拍板为「确认为缺陷改为 ||」时,条件由两轴同时变化才回写改为任一轴变化即回写;若用户给出其他替代语义,逐字应用之)。移除 02-01 铺设的 4 个回归用例的 skip 标记,使 x 正交/y 正交/斜向/传送步的坐标回写断言全部执行并通过。若用户 no-go 并提供替代回写路径:按用户语义实现替代路径,并按其语义改写回归断言。本任务是本计划唯一触碰 L0 共享代码的改动;除该行条件(或用户指定的替代实现)外不得改动 mover.ts 其他逻辑(`start` 返回 null、`stop` 兑现时机等语义是 02-03 打断机制的依赖事实
,per RESEARCH Pitfall 5
)。
pnpm exec vitest run "packages-user/data-common/src/common/mover.test.ts"
vitest 输出出现 "failed" 字样或非零退出码;或输出出现 "skipped" 计数大于 0
(说明 skip 标记未移除,回归未真正执行)
- mover.ts 回写条件与拍板记录逐字一致(行为断言)
- 4 个回归用例全部以 passed 状态出现在 vitest 输出(行为断言)
- mover.ts 中除拍板授权的改动外无其他 diff
:`git diff packages-user/data-common/src/common/mover.ts` 仅含回写条件一处变更
(CLI 断言)
L0 回归 4/4 绿,多步移动自此能以真实坐标计算后续步骤。
Task 2: 授权的 types.ts 返回类型修正 + 有向图构建(注入 IPassPredicate + useDirGroup + 终端节点 + 边界守卫)
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
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
【授权修正,先行】`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 3
,D-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 任何符号(防循环依赖)。
pnpm exec vitest run "packages-user/data-system/src/path/graph.test.ts"
vitest 输出出现 "failed" 字样或非零退出码——单向门、终端节点或边界守卫断言未通过
- path/types.ts 的 git diff 仅含 moveTo/teleportTo 两处返回类型追加 `| null`
,无其他变更
(CLI 断言)
- 单向门用例:同一条边从 A→B 判定可行、从 B→A 判定不可行(行为断言)
- shouldHit=true 的可通行图块出现在路径终点时合法、出现在中间节点时被拒绝(行为断言)
- 未注入谓词(默认)时所有边不可行;注入谓词后邻域方向数与 `DirectionMapper.map(InternalDirectionGroup.Dir4)` 一致为 4
(行为断言
)
- 越界/空数据输入触发 logger.warn 新码并得到空结果而非异常(行为断言)
- graph.ts 中无任何来自 data-state 的引入:对文件运行来源断言找不到 data-state 引用语句(源断言)
授权修正落地且 types.ts 其余零改动;有向图构建在 Node 下全绿,边语义与注入谓词同源。
Task 3: 最小损失搜索 + 仅取路径 + 回退策略槽位
(L2 核心,签名照用户 types.ts
)
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
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
创建 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) 自写 Dijkstra
(O(V²),约 40 行,零依赖)在 Task 2 的有向图上求两点间损失最小路径,输出 `IPathfindingStep[]` 步骤序列(每步 `{ dir, from, to }`,非坐标列表),满足 D-01/D-02 语义;(2) 损失函数注入槽位,签名 `PathCostFunction = (block: ILayerLocation) => number`,默认每格损失 1
(D-01
);损失守卫——函数返回非有限数或负数时 `logger.warn` 新码并按默认损失 1 处理(威胁 T-02-02
);(3) 仅取路径 `getPath(target): IPathfindingStep[]`:绑定地图与 `IObjectMovable` 后可调用
,返回步骤序列
,不调用任何移动方法
(D-06
,D-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` 占位实现
(02-03 Task 4 接管语义);(7) 每次寻路动态构建图、不缓存
(CONTEXT 酌情项默认建议
,数据端状态可变
)。system.test.ts 覆盖:默认损失 1 的最短路径步骤序列、自定义损失改变选路
(构造「步数少但损失高」的岔路
)、不可达返回空数组、moveTo/teleportTo 无路径或未绑定 movable 时返回 null
(而非异常
)、回退策略以路径步骤序列为入参被调用并生效、null 策略默认逐步、未绑定地图/对象时 logger.warn + 空结果
(mapDamage.ts:169-173 守卫形态)。
pnpm exec vitest run "packages-user/data-system/src/path/system.test.ts"
vitest 输出出现 "failed" 字样或非零退出码——最小损失、自定义损失、空数组语义、null 契约或回退策略断言未通过
- 默认损失下返回格数最少的 IPathfindingStep 序列;注入自定义损失后选择损失更小的岔路(行为断言)
- 不可达目标 getPath 返回空数组且移动对象坐标不变(行为断言)
- moveTo/teleportTo 在无路径/未绑定 movable 时返回 null
,有路径时返回含 controller 与 path 的 IPathfindingController
(行为断言
)
- PathFallbackPolicy 收到步骤序列并改变决策结果;注入 null 时决策为逐步(行为断言)
- 未绑定依赖时 warn 码输出且返回空结果不抛异常(行为断言)
- `pnpm exec vitest run "packages-user/data-system/src/path"` 全目录绿
(CLI 断言)
L2 寻路核心在 Node 下全绿:图构建、最小损失、仅取路径步骤序列、空数组语义、控制器包装契约、回退决策齐备,签名与用户 types.ts 逐字一致。
<threat_model>
Artifacts this phase produces
- 接口/类型:用户已提交的
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
Trust Boundaries
| Boundary |
Description |
| 地图/存档数据 → 图构建 |
畸形图层/掩码/越界坐标进入 graph.ts |
| 调用方注入的损失函数 → Dijkstra |
非有限/负损失值破坏搜索不变式 |
STRIDE Threat Register
| Threat ID |
Category |
Component |
Severity |
Disposition |
Mitigation Plan |
| T-02-01 |
Tampering/DoS |
graph.ts 构建与查询入口 |
medium |
mitigate |
IMapLayer.inMap + isNil 守卫,非法输入 logger.warn 新码后返回空图/空路径(Task 2) |
| T-02-02 |
Tampering |
自定义损失函数 → Dijkstra |
medium |
mitigate |
损失值守卫:非有限数/负数 → logger.warn 新码并按默认损失 1 处理(Task 3) |
| T-02-03 |
DoS |
搜索迭代规模 |
low |
mitigate |
图节点受 width×height 网格上限天然约束;Dijkstra 主循环以节点数上界终止,不存在无限循环路径 |
| T-02-SC |
Tampering |
npm/pip/cargo installs |
low |
accept |
本计划零新依赖(RESEARCH Package Legitimacy Audit:无安装对象) |
| </threat_model> |
|
|
|
|
|
- `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/path"` 全绿
- `pnpm check:type 2>&1 | Select-String -Pattern "src[\\/]path[\\/]|mover\.ts"` 无输出
(Phase 01 同款过滤式类型门,仓库既有诊断不在本计划范围)
- `pnpm check:circular` 输出中不含 path 相关路径
(eventStore 既有循环为 Phase 01 基线,维持不变)
<success_criteria>
L0 缺陷按拍板修复且有回归护栏;path/types.ts 仅含授权的返回类型修正;L2 寻路核心全部行为(有向边、单向门、终端节点、useDirGroup/Dir4 邻域、最小损失、自定义损失、仅取路径步骤序列、空数组、控制器包装契约、回退决策)在 Node 下测试验证通过,签名与用户 types.ts 逐字一致。
</success_criteria>
Create `.planning/phases/02-pathfinding/02-02-SUMMARY.md` when done