docs(02): create phase plan

This commit is contained in:
unanmed 2026-09-09 14:57:18 +08:00
parent 1dc154a2ba
commit 345c449373
5 changed files with 543 additions and 4 deletions

View File

@ -80,8 +80,25 @@ Plans:
2. 移动端点击地图上的可达格时,角色自动寻路移动到该格
3. 寻路正确避开不可通行格(碰撞/障碍/墙体)
**Plans**: TBD
**Plans**: 3 plans
**UI hint**: yes
Plans:
- [ ] 02-01-PLAN.md — 接口草案 + mover.ts:651 缺陷调查 + 回归脚手架 + D-07 用户拍板关卡checkpoint
- [ ] 02-02-PLAN.md — L0 坐标回写缺陷修复 + L2 寻路核心(有向图 + 最小损失 + 仅取路径 + 回退策略槽位)
- [ ] 02-03-PLAN.md — L3 接线(逐步/瞬移/回退默认策略/D-08 双语义/打断接管)+ barrel/logger 装配 + 阶段门禁
**Wave 1**
- [ ] 02-01-PLAN.md — 接口草案与拍板关卡autonomous: false
**Wave 2** *(blocked on Wave 1 用户拍板)*
- [ ] 02-02-PLAN.md — L0 修复 + L2 寻路核心
**Wave 3** *(blocked on Wave 2)*
- [ ] 02-03-PLAN.md — L3 接线与阶段门禁
### Phase 3: 数据端完成

View File

@ -0,0 +1,170 @@
---
phase: 02-pathfinding
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- .planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md
- packages-user/data-common/src/common/mover.test.ts
autonomous: false
requirements: [PATH-01, PATH-02]
estimate:
tokens: 45000
raw_tokens: 45000
tasks: 3
confidence: low
must_haves:
truths:
- "接口草案文档存在,含完整可拍板的签名清单(寻路系统接口、损失函数、回退策略、通行性谓词类型)与文件归属层提案,生产代码零改动 (D-01, D-02, D-03, D-04, D-05, D-06)"
- "草案显式并列 D-08 OnTouch 派发方案 A直派 executor构造 IBlockEventEnv与方案 B撞击步注明对 inPass=0 目标不生效)供用户拍板 (D-08)"
- "草案含打断时序两个选项stop 后 await 兑现 vs onEnd 回调驱动与图方向性选项4 向默认 vs 8 向)(D-10)"
- "草案声明本阶段只交付数据端移动入口,渲染端点击接线属 Phase 4 (D-11)"
- "mover.ts:651 坐标回写条件缺陷(&& 疑为 ||)的调查结论与 go/no-go 问题已呈现在草案中 (D-07)"
- "执行在本计划末尾的 checkpoint 处停止,用户逐项拍板前不开始任何实现任务 (D-07)"
artifacts:
- ".planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md"
- "packages-user/data-common/src/common/mover.test.ts"
key_links:
- "02-02 与 02-03 的全部实现任务依赖本计划 checkpoint 的用户拍板结果;拍板修改由执行者逐字应用"
assumptions:
- "PATH-01flagged assumptionspec-less probe 未分类):引擎支持自动寻路——本阶段以数据端寻路系统实现且 Node 可验证,覆盖于 02-02/02-03"
- "PATH-02flagged assumptionspec-less probe 未分类):移动端点击地图触发自动寻路——本阶段仅交付数据端移动入口供渲染端调用,点击接线按 D-11 延迟至 Phase 4 渲染适配"
prohibitions:
- "不得为寻路引入任何新 npm 依赖零依赖约束Dijkstra 自写约 40 行)"
- "不得把渲染端点击处理接进数据端代码 (D-11)"
- "未经用户拍板不得实现 D-08 OnTouch 派发方案或 mover.ts:651 修复 (D-07)"
---
<objective>
为寻路系统产出可拍板的接口草案(含全部待决选项),实证确认 L0 mover.ts:651 坐标回写缺陷并铺设其回归测试脚手架,然后在 checkpoint 处停止等待用户对接口签名、文件归属、P1 修复 go/no-go、D-08 方案 A/B、打断时序与图方向性逐项拍板。
Purpose: D-07 明锁「AI 起草 → 用户修改拍板后才实现」;本计划是全阶段唯一的 Wave 1是后续所有实现计划的闸门。
Output: 02-INTERFACE-DRAFT.md草案 + 决策问题清单、mover.test.ts 回归脚手架skip 状态)、用户拍板记录。
</objective>
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/02-pathfinding/02-CONTEXT.md
@.planning/phases/02-pathfinding/02-RESEARCH.md
@.planning/phases/02-pathfinding/02-PATTERNS.md
# 草案要引用的既有接口面(只读,不修改)
@packages-user/data-common/src/common/mover.ts
@packages-user/data-common/src/store/types.ts
@packages-user/data-common/src/common/types.ts
@packages-user/data-common/src/event/types.ts
@packages-user/data-base/src/map/types.ts
@packages-user/data-base/src/hero/types.ts
@packages-user/data-state/src/hero/moverImpl.ts
@dev.md
</context>
<tasks>
<task type="auto">
<name>Task 1: 起草寻路系统接口草案与决策问题清单</name>
<files>.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md</files>
<read_first>packages-user/data-common/src/common/mover.ts, packages-user/data-common/src/store/types.ts, packages-user/data-common/src/common/types.ts, packages-user/data-common/src/event/types.ts, packages-user/data-base/src/map/types.ts, packages-user/data-base/src/hero/types.ts, packages-user/data-state/src/hero/moverImpl.ts, .planning/phases/02-pathfinding/02-RESEARCH.md, .planning/phases/02-pathfinding/02-PATTERNS.md, dev.md</read_first>
<action>创建 02-INTERFACE-DRAFT.md中文含 TS 声明代码块全部命名标注「DRAFT 待拍板」。内容六节:
(1) 接口签名草案——按 dev.md 命名/注释/类型规范书写完整 interface/type 声明:寻路系统主接口(绑定 `IMapLayer``IObjectMovable``useXxx()` 注入槽位per D-03仅取路径方法 per D-06逐步/瞬移两种移动方式入口 per D-04打断入口 per D-10、损失函数类型默认每格损失 1per D-01、回退策略函数类型入参为路径及每步到达位置per D-05、通行性谓词类型L2 不 import L3谓词由 L3 注入per RESEARCH 分层论证)。绑定面直接引用既有 `IObjectMovable`/`IMapLayer`/`ITileLocator`/`ObjectMoveStep`/`EventTrigger`,勿重定义。
(2) 文件归属提案——L2 `packages-user/data-system/src/pathfinding/`types/graph/system/index+ L3 `packages-user/data-state/src/pathfinding/heroPathfinding.ts`RESEARCH 建议,用户可改)。
(3) D-08 两方案并列——方案 A到达相邻格后直接构造 `IGameEventInvocation`trigger=`EventTrigger.OnTouch`heroLocator=相邻格triggerLocator=目标格env 构造逐字复刻 moverImpl.ts:206-222 `commonTrigger` 形态调 `executor.execute`;方案 B追加朝向目标的撞击步复用 hit 链,并注明 P2 结论——`inPass=0` 真 no-pass 格走 `CannotMove``cannotEnter` 为空实现),方案 B 对本场景不生效。推荐 A由用户拍板 (D-08)。
(4) P1 缺陷调查——逐字引用 mover.ts:651 条件 `this.tile.x !== loc.x && this.tile.y !== loc.y`,给出「正交步永不回写坐标 → 多步寻路自第二步起以陈旧原点计算」的推理链,提出修复为 `||`,列出 go/no-go 问题(若非缺陷须由用户提供替代回写语义)。
(5) 打断时序两选项——选项 1`stop()` 后 await 兑现再查新位置起新寻路;选项 2`onEnd` 回调驱动。注明 mover.start() 移动中返回 nullmover.ts:669-670与 stop 在当前步完成后兑现的语义约束 (D-10)。
(6) 图方向性选项——默认仅 4 正交向(与 PassBit 四位掩码一致moverImpl canPass 对斜向直接放行不做图边vs 含 8 向;列利弊供拍板。
禁止在本任务修改任何 packages/ 或 packages-user/ 下的生产代码草案是唯一交付物per D-07实现待拍板</action>
<verify>
<automated>if (!(Test-Path ".planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md")) { exit 1 }; $c = Get-Content -Raw ".planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md"; if (!($c -match "IPathfindingSystem")) { exit 1 }; if (!($c -match "方案 A")) { exit 1 }; if (!($c -match "方案 B")) { exit 1 }; if (!($c -match "mover\.ts:651")) { exit 1 }</automated>
<fails_when>命令以退出码 1 结束——草案文件不存在,或正文中找不到 IPathfindingSystem 签名、D-08 方案 A/B 并列、或 mover.ts:651 缺陷引用任一项</fails_when>
</verify>
<acceptance_criteria>
- 草案文件存在且含完整 interface/type 声明代码块,覆盖绑定槽位、仅取路径、两种移动方式、打断入口(行为断言)
- D-08 方案 A 与方案 B 同时出现且各有语义说明与推荐标注(行为断言)
- P1 缺陷含逐字代码引用、推理链与 go/no-go 问题(行为断言)
- 生产源码目录无任何本任务改动:`git status --porcelain packages-user packages` 输出为空CLI 断言)
</acceptance_criteria>
<done>02-INTERFACE-DRAFT.md 完整覆盖六节内容,生产代码零改动。</done>
</task>
<task type="auto">
<name>Task 2: 铺设 L0 mover 坐标回写回归测试脚手架skip 状态)</name>
<files>packages-user/data-common/src/common/mover.test.ts</files>
<read_first>packages-user/data-common/src/common/mover.ts, packages-user/data-system/src/event/eventDispatch.test.ts, packages-user/data-common/src/store/types.ts, dev.md</read_first>
<action>新建 L0 回归测试文件,完整编写 4 个用例但以 `it.skip` 状态提交(套件保持绿色,待 02-02 修复后翻绿):(a) 正交步(仅 x 变化)后 `setPos` 回写 x(b) 正交步(仅 y 变化)后回写 y(c) 斜向步后双轴回写;(d) 传送步(`ObjectMoveType.Teleport`)后双轴回写。测试桩逐字复刻 eventDispatch.test.ts:19-33 模式:`vi.hoisted` 内 `vi.stubGlobal('main', { replayChecking: true })``vi.stubGlobal('location', { origin: 'http://localhost' })`、`Map.prototype.getOrInsertComputed` polyfill、动态 import 被测模块。每个 `it` 前必须有单行中文注释说明覆盖内容dev.md 硬规则CRLF 换行;普通引入无 `import type`。用例构造最小 ObjectMover 子类驱动 `moveProgress` 所需的步骤与钩子,不引入任何新依赖。本任务不改 mover.ts 生产代码——缺陷修复属 02-02须经用户 go/no-goD-07</action>
<verify>
<automated>pnpm exec vitest run "packages-user/data-common/src/common/mover.test.ts"</automated>
<fails_when>vitest 输出出现 "failed" 字样或非零退出码;或 4 个用例未以 skipped 状态出现(说明 skip 脚手架写错导致用例实际执行并失败)</fails_when>
</verify>
<acceptance_criteria>
- 测试文件存在且含 4 个 skip 用例,分别覆盖 x 正交、y 正交、斜向、传送步的坐标回写(行为断言)
- 测试桩包含 main.replayChecking stub、location stub 与 getOrInsertComputed polyfill行为断言
- `git status --porcelain packages-user/data-common/src/common/mover.ts` 输出为空——生产代码未被修改CLI 断言)
</acceptance_criteria>
<done>mover.test.ts 以 4 个 skip 用例通过 vitest0 failed生产代码零改动。</done>
</task>
<task type="checkpoint:human-verify" gate="blocking-human">
<name>Task 3: 用户拍板接口草案与全部待决选项D-07 关卡)</name>
<files>.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md</files>
<read_first>.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md</read_first>
<action>停止执行,向用户逐项呈现并等待拍板(用户可直接编辑草案文件,执行者须逐字应用其修改):
1. 接口签名清单——主接口命名、方法签名、注入槽位命名、损失/回退/谓词函数类型签名D-01…D-06 的接口面);
2. 文件归属层——L2/L3 文件放置提案或用户调整;
3. P1 修复 go/no-go——mover.ts:651 条件是否确认为缺陷并授权改为 `||`(若否,用户提供替代坐标回写语义);
4. D-08 OnTouch 派发——方案 A直派 executor或方案 B撞击步
5. 打断时序——stop 后 await 兑现,或 onEnd 回调驱动;
6. 图方向性——仅 4 正交向,或含 8 向。
将用户每项决定(含「按草案原样」)追加记录到草案文件末尾「拍板记录」节。未获全部六项拍板前,不得开始 02-02/02-03 的任何实现任务。</action>
<what-built>寻路系统接口草案含签名清单、文件归属、D-08 两方案、P1 go/no-go、打断时序、图方向性六项待决事项已就绪等待用户逐项拍板</what-built>
<how-to-verify>阅读 .planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md 全文,对文中六个决策项逐一给出结论(可直接编辑草案文件修改签名/命名/归属,执行者将逐字应用)</how-to-verify>
<resume-signal>逐项回复六项决策结论(或「按草案原样」),或直接修改草案文件后回复 approved</resume-signal>
<verify>
<automated>$c = Get-Content -Raw ".planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md"; if (!($c -match "拍板记录")) { exit 1 }; if (!($c -match "go/no-go")) { exit 1 }; if (!($c -match "方案 [AB]")) { exit 1 }</automated>
<fails_when>命令以退出码 1 结束——草案文件末尾没有「拍板记录」节,或六项决策中任一项缺少用户的明确结论</fails_when>
</verify>
<acceptance_criteria>
- 用户对六项决策接口签名、文件归属、P1 go/no-go、D-08 方案、打断时序、图方向性)各给出明确结论(人工确认)
- 拍板记录节完整记录每项结论及用户对草案的直接修改(行为断言)
</acceptance_criteria>
<done>六项决策全部经用户拍板并记录在案02-02/02-03 可依拍板结果执行。</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| 地图/存档数据 → 寻路图构建 | 不可信或损坏的地图数据越界坐标、缺失图层、畸形掩码将进入图构建与搜索02-02/02-03 消费) |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|-----------|----------|-----------|----------|-------------|-----------------|
| T-02-01 | Tampering/DoS | 图构建入口02-02 graph.ts | medium | mitigate | 草案中明确边界守卫要求:`IMapLayer.inMap` 守卫 + `isNil` 判空 + 非法输入 logger.warn 数字码后返回空路径(本任务落草案,实现落 02-02 |
| T-02-02 | Tampering | 自定义损失函数02-02 system.ts | medium | mitigate | 草案中明确损失值守卫:非有限数/负数 → logger.warn 新码并按默认损失 1 处理(实现落 02-02 |
| T-02-SC | Tampering | npm/pip/cargo installs | low | accept | 本阶段零新依赖RESEARCH Package Legitimacy Audit无安装对象无供应链面 |
</threat_model>
<verification>
- 草案文件存在且六节齐备D-08 两方案、打断时序、图方向性均已并列呈现
- mover.test.ts 4 用例 skip 通过,生产代码零改动
- 「拍板记录」节含全部六项用户结论
</verification>
<success_criteria>
用户在 checkpoint 处完成六项拍板;接口草案与 P1 调查成为 02-02/02-03 的执行依据;本计划全程未修改任何生产代码。
</success_criteria>
<output>
Create `.planning/phases/02-pathfinding/02-01-SUMMARY.md` when done
</output>

View File

@ -0,0 +1,173 @@
---
phase: 02-pathfinding
plan: 02
type: execute
wave: 2
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
autonomous: true
requirements: [PATH-01, PATH-02]
estimate:
tokens: 70000
raw_tokens: 70000
tasks: 3
confidence: low
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)"
- "不可达目标按 D-08 情形 2 返回空数组且不移动 (D-08)"
- "回退策略槽位存在:策略函数接收路径及每步到达位置并决策移动方式(瞬移或退为逐步)(D-04, D-05)"
- "绑定面为 IMapLayer 与 IObjectMovable 注入槽位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"
key_links:
- "graph.ts 消费注入的通行性谓词而非 import L3谓词复刻 moverImpl canPass 的掩码语义(事件层恒参与,其余层仅 onlyEvents 参与)"
- "system.ts 的损失/回退/谓词槽位由 02-03 的 L3 接线注入"
assumptions:
- "PATH-01flagged assumptionspec-less probe 未分类):本计划交付自动寻路的纯计算核心(图构建 + 最小损失搜索 + 仅取路径Node 可验证"
- "PATH-02flagged assumptionspec-less probe 未分类):本计划提供的仅取路径 API 即渲染端 Phase 4 将调用的数据端入口之一,点击接线不在本阶段 (D-11)"
prohibitions:
- "不得为寻路引入任何新 npm 依赖Dijkstra 自写,零新包)"
- "不得把渲染端点击处理接进数据端代码 (D-11)"
- "接口签名必须与用户拍板后的草案逐字一致,不得自行更名或改层 (D-07)"
---
<objective>
按用户拍板结果修复 L0 mover.ts:651 坐标回写缺陷并翻绿回归测试,然后在 data-systemL2落地寻路核心有向图构建注入谓词、最小损失搜索自定义损失、默认每格 1、仅取路径 API、D-08 情形 2 空数组语义、回退策略槽位与移动方式决策。
Purpose: 这是全部寻路行为的纯逻辑地基——不移动任何对象,可在 Node 下完整验证L0 缺陷不修则 02-03 的多步逐步移动必然走崩RESEARCH P1
Output: 修复后的 mover.ts + 绿色 L0 回归pathfinding/{types,graph,system,index}.ts 及 co-located 测试。
</objective>
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/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
# 草案与拍板记录是本计划的接口事实源
@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
</context>
<tasks>
<task type="tracer">
<name>Task 1: 按拍板结果修复 L0 坐标回写缺陷并翻绿回归测试</name>
<files>packages-user/data-common/src/common/mover.ts, packages-user/data-common/src/common/mover.test.ts</files>
<read_first>.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</read_first>
<action>依据 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</action>
<verify>
<automated>pnpm exec vitest run "packages-user/data-common/src/common/mover.test.ts"</automated>
<fails_when>vitest 输出出现 "failed" 字样或非零退出码;或输出出现 "skipped" 计数大于 0说明 skip 标记未移除,回归未真正执行)</fails_when>
</verify>
<acceptance_criteria>
- mover.ts 回写条件与拍板记录逐字一致(行为断言)
- 4 个回归用例全部以 passed 状态出现在 vitest 输出(行为断言)
- mover.ts 中除拍板授权的改动外无其他 diff`git diff packages-user/data-common/src/common/mover.ts` 仅含回写条件一处变更CLI 断言)
</acceptance_criteria>
<done>L0 回归 4/4 绿,多步移动自此能以真实坐标计算后续步骤。</done>
</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>
<verify>
<automated>pnpm exec vitest run "packages-user/data-system/src/pathfinding/graph.test.ts"</automated>
<fails_when>vitest 输出出现 "failed" 字样或非零退出码——单向门、终端节点或边界守卫断言未通过</fails_when>
</verify>
<acceptance_criteria>
- 单向门用例:同一条边从 A→B 判定可行、从 B→A 判定不可行(行为断言)
- eventPass=false 的可通行图块出现在路径终点时合法、出现在中间节点时被拒绝(行为断言)
- 越界/空数据输入触发 logger.warn 新码并得到空结果而非异常(行为断言)
- graph.ts 与 types.ts 中无任何来自 data-state 的引入:对两文件运行来源断言找不到 data-state 引用语句(源断言)
</acceptance_criteria>
<done>有向图构建在 Node 下全绿,边语义与 canPass 判定同源。</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>
<verify>
<automated>pnpm exec vitest run "packages-user/data-system/src/pathfinding/system.test.ts"</automated>
<fails_when>vitest 输出出现 "failed" 字样或非零退出码——最小损失、自定义损失、空数组语义或回退策略断言未通过</fails_when>
</verify>
<acceptance_criteria>
- 默认损失下返回格数最少路径;注入自定义损失后选择损失更小的岔路(行为断言)
- 不可达目标返回空数组且移动对象坐标不变(行为断言)
- 回退策略收到路径与每步到达位置并改变决策结果(行为断言)
- 未绑定依赖时 warn 码输出且返回空结果不抛异常(行为断言)
- `pnpm exec vitest run "packages-user/data-system/src/pathfinding"` 全目录绿CLI 断言)
</acceptance_criteria>
<done>L2 寻路核心在 Node 下全绿:图构建、最小损失、仅取路径、空数组语义、回退决策齐备。</done>
</task>
</tasks>
<threat_model>
## 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>
<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 基线,维持不变)
</verification>
<success_criteria>
L0 缺陷按拍板修复且有回归护栏L2 寻路核心全部行为(有向边、单向门、终端节点、最小损失、自定义损失、仅取路径、空数组、回退决策)在 Node 下测试验证通过。
</success_criteria>
<output>
Create `.planning/phases/02-pathfinding/02-02-SUMMARY.md` when done
</output>

View File

@ -0,0 +1,173 @@
---
phase: 02-pathfinding
plan: 03
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/core.ts
- packages-user/data-state/src/index.ts
- packages-user/data-system/src/index.ts
- packages/common/src/logger.json
autonomous: true
requirements: [PATH-01, PATH-02]
estimate:
tokens: 80000
raw_tokens: 80000
tasks: 3
confidence: low
must_haves:
truths:
- "逐步自动寻路端到端可用:路径翻译为 Dir 步骤队列并复用 `DefaultHeroMoveTopImpl` 执行,每步走 enter/leave/hit hooks途经事件自然触发 (D-09)"
- "瞬移执行可用:决策允许瞬移时经 `ObjectMoveType.Teleport` 到达目标;拍板的回退策略判定需回退时自动退为逐步寻路,默认策略为路径上存在事件即回退 (D-04, D-05)"
- "D-08 情形 1no-pass 目标存在可达相邻格时,移动到该相邻格、按拍板方案派发目标 OnTouch、勇士面朝目标 (D-08)"
- "自动寻路途中被新寻路调用或显式打断入口打断后,新寻路从最新位置接管 (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/core.ts"
- "packages-user/data-state/src/index.ts"
- "packages-user/data-system/src/index.ts"
- "packages/common/src/logger.json"
key_links:
- "heroPathfinding.ts 注入 02-02 的损失/回退/谓词槽位并绑定 hero moverL3 → L2 合法方向,反向即循环依赖)"
- "D-08 OnTouch 派发走 Phase 1 source-aware 执行链executor.executeenv 形态复刻 moverImpl commonTrigger"
- "打断接管经 `IMoverController.stop()`先停旧再起新mover.start 移动中返回 null"
assumptions:
- "PATH-01flagged assumptionspec-less probe 未分类自动寻路以「fixture 地图上勇士沿最小损失路径移动且事件触发」的 Node 测试等效验证"
- "PATH-02flagged assumptionspec-less probe 未分类):本计划交付数据端移动入口并在 Node 下冒烟验证;移动端点击触发按 D-11 延迟至 Phase 4 渲染适配验证"
prohibitions:
- "不得为寻路引入任何新 npm 依赖(零依赖约束)"
- "不得把渲染端点击处理接进数据端代码 (D-11)"
- "D-08 OnTouch 派发必须按用户拍板方案实现,不得静默替换 (D-07, D-08)"
---
<objective>
在 data-stateL3完成寻路系统的移动执行接线逐步寻路复用 hero mover 与 Phase 1 事件链、瞬移与默认回退策略、D-08 情形 1 相邻格 + OnTouch + 面朝目标、打断接管,以及 CoreState/barrel 装配、logger 码登记与阶段门禁。
Purpose: 让寻路从「算得出」变成「走得通」——勇士真实移动、事件真实触发、输入真实接管;这是 PATH-01 的最终形态与 PATH-02 的数据端入口。
Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、logger.json全阶段门禁通过。
</objective>
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/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
@.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
@packages-user/data-state/src/hero/moverImpl.ts
@packages-user/data-state/src/core.ts
@packages-user/data-state/src/index.ts
@packages-user/data-system/src/index.ts
@packages-user/data-common/src/common/mover.ts
@packages-user/data-base/src/hero/mover.ts
@packages-user/data-base/src/map/types.ts
@packages/common/src/logger.json
@dev.md
</context>
<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, packages-user/data-system/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';` 之后追加寻路导出data-system/src/index.ts 追加 pathfinding 导出dev.md不转发导出。heroPathfinding.test.ts 用 eventDispatch.test.ts:105-179 fixture 模式:勇士在 fixture 地图上自动寻路走两步断言终点坐标、leave/enter 事件按 Phase 1 source-aware 顺序触发、途经带事件的图块事件被执行。签名与命名以拍板草案为唯一事实源D-07</action>
<verify>
<automated>pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts"</automated>
<fails_when>vitest 输出出现 "failed" 字样或非零退出码——勇士未到达目标、事件未按序触发或翻译步骤数与路径不符</fails_when>
</verify>
<acceptance_criteria>
- 勇士沿最小损失路径逐步移动并停在目标格,坐标断言通过(行为断言)
- 途经图块的 leave/enter 钩子触发且 env 的 heroLocator 与实际位置一致(行为断言)
- core.ts 含寻路接线且位于勇士顶层初始化 region源断言
- 两个 barrel 均导出寻路内容,`pnpm check:type 2>&1 | Select-String -Pattern "heroPathfinding|pathfinding|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>
<verify>
<automated>pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts"</automated>
<fails_when>vitest 输出出现 "failed" 字样或非零退出码——瞬移未直达、事件路径未回退逐步、OnTouch 未按拍板方案触发或面朝方向错误</fails_when>
</verify>
<acceptance_criteria>
- 无事件路径瞬移一步到达目标,坐标断言通过(行为断言)
- 含事件路径在默认策略下退为逐步且途经事件触发(行为断言)
- no-pass 目标勇士位于相邻格、朝向指向目标、OnTouch 事件以拍板方案派发且 triggerLocator 为目标格(行为断言)
- 四邻皆不可达的 no-pass 目标:路径空数组、勇士不动(行为断言)
</acceptance_criteria>
<done>D-04/D-05/D-08 全部语义在 L3 落地且测试验证。</done>
</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>
<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>
<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 断言)
</acceptance_criteria>
<done>打断接管按拍板时序可用;全量测试绿、循环依赖与 lint 门禁对本阶段文件清洁。</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|----------|-------------|
| 地图/存档数据 → OnTouch env 构造 | 目标格/图层可能为空或畸形,进入 `IBlockEventEnv` 构造 |
| 玩家输入时序 → 移动队列 | 打断与新寻路竞态可能使 mover 队列处于不一致状态 |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|-----------|----------|-----------|----------|-------------|-----------------|
| T-02-04 | Tampering | D-08 OnTouch env 构造 | low | mitigate | env 各字段按 `IBlockEventEnv` 可空语义用 `isNil` 判空填实,非法来源不构造 invocationTask 2复刻 commonTrigger 形态) |
| T-02-05 | DoS | 打断接管竞态 | low | mitigate | 单一 controller 所有权:先按拍板时序停旧移动、确认终止后起新路径;识别自然终止避免重复 stopTask 3 |
| T-02-SC | Tampering | npm/pip/cargo installs | low | accept | 本计划零新依赖RESEARCH Package Legitimacy Audit无安装对象 |
</threat_model>
<verification>
- `pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts"` 全绿
- `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 lint:user` 无新报错
</verification>
<success_criteria>
勇士在 fixture 地图上可自动寻路(逐步/瞬移/回退/不可达双语义/打断接管全部符合拍板契约);数据端入口经 barrel 导出且 Node 可验证阶段门禁全绿。PATH-01 交付完成PATH-02 以数据端入口形态交付(点击接线属 Phase 4per D-11
</success_criteria>
<output>
Create `.planning/phases/02-pathfinding/02-03-SUMMARY.md` when done
</output>

View File

@ -42,9 +42,15 @@ Type/lint gates (per Phase 01 conventions): `pnpm check:type` (filtered via `Sel
| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
| 02-01-01 | 01 | 1 | PATH-01 | — | N/A | unit | `pnpm exec vitest run "packages-user/data-system/src/pathfinding/*.test.ts"` | ❌ W0 | ⬜ pending |
| 02-01-02 | 01 | 1 | PATH-01 | — | N/A | unit (L0 regression) | `pnpm exec vitest run "packages-user/data-common/src/common/mover.test.ts"` | ❌ W0 | ⬜ pending |
| 02-02-01 | 02 | 2 | PATH-01 | — | N/A | unit | `pnpm exec vitest run "packages-user/data-state/src/hero/*.test.ts"` | ❌ W0 | ⬜ pending |
| 02-01-01 | 01 | 1 | PATH-01 | — | N/A | draft-artifact check | `if (!(Test-Path ".planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md")) { exit 1 }; ...`(见 02-01 Task 1 verify | ❌ W0 | ⬜ pending |
| 02-01-02 | 01 | 1 | PATH-01 | — | N/A | unit (skip scaffold) | `pnpm exec vitest run "packages-user/data-common/src/common/mover.test.ts"` | ❌ W0 | ⬜ pending |
| 02-01-03 | 01 | 1 | PATH-01 | — | — | checkpoint:human-verify | 人工拍板六项决策(无自动命令;拍板记录节断言) | — | ⬜ pending |
| 02-02-01 | 02 | 2 | PATH-01 | T-02-01..03 前置 | L0 回写修复 | unit (L0 regression) | `pnpm exec vitest run "packages-user/data-common/src/common/mover.test.ts"` | ❌ W0 | ⬜ pending |
| 02-02-02 | 02 | 2 | PATH-01 | T-02-01 | inMap/isNil 守卫 + warn 码 | unit | `pnpm exec vitest run "packages-user/data-system/src/pathfinding/graph.test.ts"` | ❌ W0 | ⬜ pending |
| 02-02-03 | 02 | 2 | PATH-01 | T-02-02, T-02-03 | 损失值守卫 + 网格上界 | unit | `pnpm exec vitest run "packages-user/data-system/src/pathfinding/system.test.ts"` | ❌ W0 | ⬜ pending |
| 02-03-01 | 03 | 3 | PATH-01 | — | N/A | unit (e2e slice) | `pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts"` | ❌ W0 | ⬜ pending |
| 02-03-02 | 03 | 3 | PATH-01, PATH-02 | T-02-04 | OnTouch env 判空填实 | unit | 同上 | ❌ W0 | ⬜ pending |
| 02-03-03 | 03 | 3 | PATH-01 | T-02-05 | 打断接管时序 | unit + gates | 同上; `pnpm check:type 2>&1 \| Select-String -Pattern "pathfinding\|mover\.ts\|core\.ts"` | ❌ W0 | ⬜ pending |
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*