diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index f6eb129..0a3efb7 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -122,7 +122,36 @@ Plans:
3. 数据端与渲染端保持双端分离,渲染相关代码经 `r()`/`rf()` 门控或走 hook,渲染端不向数据端推送更新
4. 接口设计由用户主导,AI 仅负责实现
-**Plans**: TBD
+**Plans**: 6 plans
+Plans:
+
+- [ ] 03-01-PLAN.md — Node-safe CoreState、内部 legacy 依赖边界与最小 replay tracer
+- [ ] 03-02-PLAN.md — 八个事件内建函数契约 checkpoint 与最小注册实现
+- [ ] 03-03-PLAN.md — 稳定 replay enum、异步 command 与 top-level 注册
+- [ ] 03-04-PLAN.md — 固定 Node 回放 fixture、首分歧 thrown diagnostic 与最终快照
+- [ ] 03-05-PLAN.md — DATA-01 focused closure 与四包 type/circular 最终门禁
+- [ ] 03-06-PLAN.md — Tile events contract 与 legacy conversion 收口
+
+**Wave 1**
+
+- [ ] 03-01-PLAN.md — Node-safe CoreState、内部 legacy 依赖边界与最小 replay tracer
+
+**Wave 2** *(blocked on Wave 1 completion)*
+
+- [ ] 03-02-PLAN.md — 八个事件内建函数契约 checkpoint 与最小注册实现
+- [ ] 03-06-PLAN.md — Tile events contract 与 legacy conversion 收口
+
+**Wave 3** *(blocked on Wave 2 completion)*
+
+- [ ] 03-03-PLAN.md — 稳定 replay enum、异步 command 与 top-level 注册
+
+**Wave 4** *(blocked on Wave 3 completion)*
+
+- [ ] 03-04-PLAN.md — 固定 Node 回放 fixture、首分歧 thrown diagnostic 与最终快照
+
+**Wave 5** *(blocked on Wave 4 completion)*
+
+- [ ] 03-05-PLAN.md — DATA-01 focused closure 与四包 type/circular 最终门禁
### Phase 4: 渲染适配与双布局
diff --git a/.planning/phases/03-data-completion/03-01-PLAN.md b/.planning/phases/03-data-completion/03-01-PLAN.md
new file mode 100644
index 0000000..475e5df
--- /dev/null
+++ b/.planning/phases/03-data-completion/03-01-PLAN.md
@@ -0,0 +1,149 @@
+---
+phase: 03-data-completion
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - packages/common/src/logger.ts
+ - packages-user/data-base/src/game.ts
+ - packages-user/data-common/src/save/memory.ts
+ - packages-user/data-state/src/legacy/dependencies.ts
+ - packages-user/data-state/src/core.ts
+ - packages-user/data-state/src/enemy/calculator.ts
+ - packages-user/data-state/src/legacy/move.ts
+ - packages-user/data-state/test/fixtures/closed-loop.ts
+ - packages-user/data-state/test/nodeTracer.test.ts
+autonomous: true
+requirements: [DATA-01]
+estimate:
+ tokens: 80000
+ raw_tokens: 80000
+ tasks: 2
+ confidence: low
+must_haves:
+ truths:
+ - "在没有 main、window、document、IndexedDB 或 legacy loading host 时,Node 可以直接导入数据端并创建两个相互独立的 CoreState 实例 (D-12, D-23, D-28)"
+ - "最小真实闭环可在 Node 中完成:固定 fixture 装配地图,ReplaySystem 执行一次玩家移动,事件改变地图状态,动作 Promise 完成后 replay 才进入结束态 (D-06, D-13)"
+ - "CoreState 只依赖内部注入的 legacy converter/data-source boundary,不直接读取 legacy 全局;无 host 时使用空的 Node-safe boundary (D-19, D-28)"
+ - "Node 状态使用内存存档适配器,不创建 Dexie 数据库或触碰 IndexedDB;浏览器/legacy host 分支保留原有兼容初始化,且 factory 不增加 options (D-18, D-23, D-28)"
+ artifacts:
+ - path: "packages-user/data-common/src/save/memory.ts"
+ provides: "不依赖 IndexedDB 的内部内存存档实现"
+ - path: "packages-user/data-state/src/legacy/dependencies.ts"
+ provides: "不读取全局的内部 legacy converter/data-source 注入边界"
+ - path: "packages-user/data-state/src/core.ts"
+ provides: "Node-safe CoreState constructor 与无参数 createCoreState()"
+ exports: ["CoreState", "createCoreState"]
+ - path: "packages-user/data-state/test/fixtures/closed-loop.ts"
+ provides: "固定初始数据与最小 replay tracer fixture"
+ - path: "packages-user/data-state/test/nodeTracer.test.ts"
+ provides: "最小 replay→move→event→map tracer 的 focused 回归入口"
+ key_links:
+ - "nodeTracer.test.ts → createCoreState() → CoreState constructor → memory save/data fixture"
+ - "private direct ReplaySystem tracer harness → hero.location.mover → existing source-aware event path → eventSetBlock → event-layer matrix"
+ - "compatibility host injection → internal legacy dependency boundary → CoreState constructor; CoreState never reads legacy globals directly"
+ - "Node host detection → logger/game/loading guards → no browser global evaluation"
+---
+
+
+先交付一个生产质量的 Node/replay 垂直 tracer:无浏览器宿主时创建新的四层数据状态,加载固定小地图,执行一次真实玩家移动并通过事件改变地图矩阵,在 Node 运行的 focused tracer 中验证成功;同时把 legacy converter/data-source 收口到内部注入 boundary。独立 Node 进程命令由 Plan 03-04 接管。
+
+Purpose: 这是后续事件内建函数、回放命令、完整 fixture 和质量门禁共同依赖的 Node-safe 骨架;不得用全局 stub 把浏览器初始化伪装成 Node 支持,也不得让 CoreState 继续读取 legacy 全局。
+Output: Node-safe import/factory、内部 legacy 依赖边界、内存存档适配器、固定 tracer fixture 和 focused tests;独立 `pnpm test:data-node` runner 由 Plan 03-04 在此 tracer 之上接线。
+Decision trace: D-01: closure coverage; D-03: explicit fixtures; D-04: fixed data tests; D-06: awaited actions; D-09: factory-first construction; D-10: top-level assembly; D-12: Node boundary; D-13: replay tracer; D-18: memory save path; D-19: injected legacy boundary; D-20: cycle closure; D-23: no-options factory; D-28: Node-safe constructor.
+
+
+
+@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
+@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/REQUIREMENTS.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/03-data-completion/03-CONTEXT.md
+@.planning/phases/03-data-completion/03-RESEARCH.md
+@dev.md
+
+@packages/common/src/logger.ts
+@packages-user/data-base/src/game.ts
+@packages-user/data-common/src/save/types.ts
+@packages-user/data-common/src/save/system.ts
+@packages-user/data-common/src/store/types.ts
+@packages-user/data-common/src/store/tileStore.ts
+@packages-user/data-state/src/core.ts
+@packages-user/data-state/src/ins.ts
+@packages-user/data-state/src/hero/moverImpl.ts
+@packages-user/data-system/src/event/eventDispatch.test.ts
+
+
+
+
+ Task 1: singleton back-edge 修复与 Node-safe CoreState construction tracer
+ packages/common/src/logger.ts, packages-user/data-base/src/game.ts, packages-user/data-common/src/save/memory.ts, packages-user/data-state/src/legacy/dependencies.ts, packages-user/data-state/src/core.ts, packages-user/data-state/src/enemy/calculator.ts, packages-user/data-state/src/legacy/move.ts
+ packages/common/src/logger.ts, packages-user/data-base/src/game.ts, packages-user/data-common/src/save/types.ts, packages-user/data-common/src/save/system.ts, packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts, packages-user/data-state/src/enemy/index.ts, packages-user/data-state/src/enemy/calculator.ts, packages-user/data-state/src/ins.ts, packages-user/data-state/src/legacy/index.ts, packages-user/data-state/src/legacy/move.ts, packages-user/data-system/src/combat/types.ts, .planning/phases/03-data-completion/03-CONTEXT.md, dev.md
+ 先完成本任务的两个反向依赖修复,再让构造 tracer 运行:移除 `packages-user/data-state/src/enemy/calculator.ts` 对 `../ins` singleton 的 import,改为使用现有 `IReadonlyEnemyHandler` 提供的 state/hero/item 数据路径;同时移除 `packages-user/data-state/src/legacy/move.ts` 对 `../index` 的 barrel import,改用不回指 data-state index 的低层依赖或显式状态边界。随后按 D-18 的 Node memory/reset policy 以及 D-19、D-23、D-28 改造 Node 边界:在 `packages-user/data-state/src/core.ts` 导出无参数 `createCoreState(): CoreState`,函数直接返回 `new CoreState()`;Node probe 与后续 runner 必须从 `src/core.ts` 直接导入 factory,不能经由会求值 `ins` 的兼容 barrel。兼容 barrel 的 factory/replay/event 导出由 Plan 03-03 的顶层装配任务负责,且必须保留浏览器兼容 singleton。CoreState 的主要初始化顺序仍留在 constructor。建立内部集中 legacy converter/data-source dependency boundary:CoreState 只能消费该 boundary,不能读取 `core` 或其他 legacy 全局;浏览器兼容装配通过内部注入路径提供 legacy 依赖,Node 无 host 时提供不加载 legacy 的 memory-safe boundary。不得把依赖 bundle 暴露为 factory options,也不得新增 singleton、不得把完整 data-state barrel 重新引入低层模块、不得改变用户-owned interface。检测不到 legacy/browser host 时,constructor 不注册会读取 `core` 的 loading 回调、不调用 Dexie/IndexedDB 初始化,并使用内部 `MemorySaveSystem`;检测到浏览器/legacy host 时保留兼容路径。修复 logger 与 data-base game loading 模块的模块求值和日志调用,使缺少 `main`、`window`、`document`、`location` 时不会抛异常,也不引入 DOM shim;这些步骤必须按“back-edge repair → Node-safe CoreState construction”顺序完成 (D-09, D-12, D-18, D-19, D-20, D-23, D-28)。
+
+ $graph = pnpm exec madge --json --extensions ts packages-user/data-state/src/enemy/calculator.ts packages-user/data-state/src/legacy/move.ts | ConvertFrom-Json; $canonical = { param([string]$path) [void]($parts = ($path -replace '\\','/').Split('/')); [void]($stack = New-Object 'System.Collections.Generic.List[string]'); foreach ($part in $parts) { if ([string]::IsNullOrEmpty($part) -or $part -eq '.') { continue }; if ($part -eq '..') { if ($stack.Count -gt 0 -and $stack[$stack.Count - 1] -ne '..') { [void]$stack.RemoveAt($stack.Count - 1) } else { [void]$stack.Add('..') } } else { [void]$stack.Add($part) } }; $stack -join '/' }; $canonicalGraph = @{}; foreach ($node in $graph.PSObject.Properties) { $sourceKey = [string]$canonical.Invoke([string]$node.Name)[-1]; $canonicalGraph[$sourceKey] = @($node.Value | ForEach-Object { [string]$canonical.Invoke([string]$_)[-1] }) }; $forbidden = @(@('enemy/calculator.ts','ins.ts'), @('legacy/move.ts','index.ts')); $violations = @(); foreach ($spec in $forbidden) { $sourceKey = [string]$canonical.Invoke($spec[0])[-1]; $targetKey = [string]$canonical.Invoke($spec[1])[-1]; if (-not $canonicalGraph.ContainsKey($sourceKey)) { throw "Missing dependency-graph node $($spec[0])" }; if (@($canonicalGraph[$sourceKey]) -contains $targetKey) { $violations += "Forbidden exact edge $($spec[0]) -> $($spec[1])" } }; if ($violations.Count -gt 0) { throw ($violations -join '; ') }; pnpm exec tsx -e "import { createCoreState } from './packages-user/data-state/src/core.ts'; const first = createCoreState(); const second = createCoreState(); if (first === second) throw new Error('shared CoreState');"
+ 依赖图仍包含精确的 canonical `enemy/calculator.ts -> ins.ts` singleton edge 或 `legacy/move.ts -> index.ts` barrel edge、任一低层文件仍回指 data-state barrel、Node import/construction 触碰浏览器 host 或 IndexedDB、两个 factory 实例相同,或 probe 非零
+
+
+ - `calculator.ts` 不再导入 `ins`,`legacy/move.ts` 不再导入 data-state barrel,且两条 back-edge 的具体路径已由测试命令检查 (D-20)
+ - CoreState 的直接依赖图已经可安全求值,Node 无 host 时可创建两个独立实例并走 memory-safe constructor (D-12, D-23, D-28)
+
+ CoreState 构造路径所依赖的 enemy 与 legacy singleton back-edge 已在 Wave 1 明确闭合。
+
+
+
+ Task 2: Node-safe CoreState 与最小 replay→move→event→map tracer
+ packages-user/data-state/test/fixtures/closed-loop.ts, packages-user/data-state/test/nodeTracer.test.ts
+ packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts, packages-user/data-state/test/fixtures/closed-loop.ts, packages-user/data-common/src/save/types.ts, packages-user/data-common/src/replay/types.ts, packages-user/data-common/src/replay/system.ts, packages-user/data-common/src/replay/sandbox.ts, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-system/src/event/eventDispatch.test.ts, .planning/phases/03-data-completion/03-CONTEXT.md, dev.md
+ 消费 Task 1 已验证的 Node-safe factory 与内部 legacy boundary,创建固定小地图 fixture,使用仅属于本 tracer 的私有 direct `ReplaySystem` harness,不提前占用 Plan 03 的 top-level command registry;显式装配 tile/map/event 数据,构造一个可等待的 replay movement command,调用现有 hero mover 和 source-aware event executor,令事件执行 `eventSetBlock` 改变 event layer 矩阵。不要在本任务增加 `script/test-data-node.ts` 或 package script;独立 Node 进程 runner 由 Plan 03-04 接线。新增 `nodeTracer.test.ts` 使用显式 fixture/依赖注入,并在每个 `it` 调用前写一行中文注释说明覆盖内容 (D-01, D-03, D-06, D-09, D-10, D-12, D-13, D-23, D-28)。
+
+ pnpm exec vitest run packages-user/data-state/test/nodeTracer.test.ts
+ 测试非零退出、Node tracer 触碰 DOM/IndexedDB、没有创建新 CoreState、没有等待移动 Promise、事件没有改变固定地图矩阵、录像未正常结束,或任一 `it` 缺少中文覆盖注释
+
+
+ - `createCoreState()` 无参数且每次返回独立 `CoreState`,直接调用 `new CoreState()`;导入与构造在 Node 无全局 stub 也成功 (D-23, D-28)
+ - CoreState 不直接读取 legacy 全局;兼容 host 与 Node 路径均通过内部 dependency boundary 提供数据源 (D-19, D-28)
+ - tracer 覆盖初始化、玩家动作、事件/状态变化和正常结束四个边界,且动作 Promise 完整兑现后 replay 才结束 (D-06, D-13)
+ - focused tests 使用显式 fixture/依赖注入,所有 `it` 前都有中文覆盖注释,未扩大到 Phase 4/5/6 (D-01, D-03, D-04)
+
+ Wave 1 已证明修复 back-edge 后的 Node-safe constructor/factory 与最小 replay tracer 可重复通过;独立 Node runner 由后续计划接管。
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|---|---|
+| Node runner → data factory | runner supplies fixture data and replay route to an unrendered runtime |
+| legacy/browser host → CoreState constructor | optional host globals can otherwise trigger browser loading or IndexedDB |
+| raw tile fixture → TileStore | fixture values become indexed tile/event state |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
+|---|---|---|---|---|---|
+| T-03-01 | Tampering/DoS | fixture raw tile/map data | medium | mitigate | Reuse existing raw map validation and explicit tile/event fixture construction; malformed data returns safe failure before replay. |
+| T-03-02 | Information disclosure | Node logger/browser boundary | medium | mitigate | Guard DOM/location/main access and keep Node path free of browser loading/IndexedDB initialization. |
+| T-03-03 | Tampering | replay tracer command | medium | mitigate | Await command completion, validate route step and return non-zero on the first failed step. |
+| T-03-SC | Tampering | npm/pip/cargo installs | low | accept | No package installation is planned; existing dependencies only. |
+
+
+
+ - `pnpm exec vitest run packages-user/data-state/test/nodeTracer.test.ts`
+ - `pnpm check:type` may still report pre-existing unrelated render/legacy diagnostics; no new diagnostic is accepted in the files owned by this plan.
+
+
+
+Node can create and replay a fresh data state without browser globals, the Wave 1 tracer proves a real awaited movement/event/map mutation path, and CoreState reaches legacy data only through the internal injected boundary while preserving the no-options factory contract; the dedicated process runner is explicitly left to Plan 03-04.
+
+
+
diff --git a/.planning/phases/03-data-completion/03-02-PLAN.md b/.planning/phases/03-data-completion/03-02-PLAN.md
new file mode 100644
index 0000000..594ef6c
--- /dev/null
+++ b/.planning/phases/03-data-completion/03-02-PLAN.md
@@ -0,0 +1,151 @@
+---
+phase: 03-data-completion
+plan: 02
+type: execute
+wave: 2
+depends_on: [03-01]
+files_modified:
+ - .planning/phases/03-data-completion/03-EVENT-CONTRACT.md
+ - packages-user/data-state/src/event/types.ts
+ - packages-user/data-state/src/event/map.ts
+ - packages-user/data-state/src/event/hero.ts
+ - packages-user/data-state/src/event/event.ts
+ - packages-user/data-state/src/event/index.ts
+ - packages-user/data-state/src/event/event.test.ts
+ - packages-user/data-system/src/event/system.ts
+ - packages-user/data-state/src/core.ts
+autonomous: false
+requirements: [DATA-01]
+estimate:
+ tokens: 65000
+ raw_tokens: 65000
+ tasks: 2
+ confidence: low
+must_haves:
+ truths:
+ - "八个批准的 event built-ins 使用 `(param, env)` 与 `IBlockEventEnv`,失败时安全跳过并返回 void (D-24, D-27)"
+ - "地图、玩家、事件三组 built-ins 的行为只覆盖批准的闭环,不扩展完整 legacy 事件目录 (D-11, D-24)"
+ - "默认 built-in 注册由 data-state 事件模块提供注册项,CoreState/GameEventSystem 负责最终装配顺序,不把每个函数实现复制到 CoreState (D-10, D-11)"
+ artifacts:
+ - path: "packages-user/data-state/src/event/types.ts"
+ provides: "每个 built-in 的独立参数接口"
+ - path: "packages-user/data-state/src/event/map.ts"
+ provides: "eventSetBlock 与 eventMoveBlock"
+ - path: "packages-user/data-state/src/event/hero.ts"
+ provides: "eventMoveHero、eventMoveHeroStep、eventTouchFront"
+ - path: "packages-user/data-state/src/event/event.ts"
+ provides: "eventInsertEvents 与 eventInsertEvent"
+ - path: ".planning/phases/03-data-completion/03-EVENT-CONTRACT.md"
+ provides: "checkpoint 后的字段/失败/注册语义记录"
+ key_links:
+ - "event module registration items → GameEventSystem AnonTokyo BuiltInFunction map"
+ - "IBlockEventEnv.state/heroFloor/map/layer/tile → safe target resolution"
+ - "built-in async movement → awaited hero mover controller/event executor"
+---
+
+
+在已有 Node tracer 之上补齐用户批准的最小事件内建函数集合,并用显式 fixture 覆盖地图控制、玩家控制和事件控制。先用 blocking checkpoint 将已锁定的八个名称、`(param, env)`/`IBlockEventEnv`、字段、注册 seam 与安全失败语义完整记录,任何仍未记录的字段必须在实现前得到用户确认。
+
+Purpose: 让事件解释器拥有闭环所需的最小状态操作,同时保留 Phase 5 legacy 迁移与 Phase 6 全覆盖边界。
+Output: 参数类型、八个 built-ins、默认注册项、focused behavior tests 和用户确认记录。
+Decision trace: D-01: closure coverage; D-02: stop on undefined contracts; D-03: explicit fixtures; D-04: fixed data tests; D-06: awaited event actions; D-10: top-level registration; D-11: approved event scope; D-12: Node-safe behavior; D-17: blocking clarification; D-24: approved event groups; D-27: `(param, env)` and safe void.
+
+
+
+@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
+@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/REQUIREMENTS.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/03-data-completion/03-CONTEXT.md
+@.planning/phases/03-data-completion/03-RESEARCH.md
+@packages-user/data-state/src/event/types.ts
+@packages-user/data-state/src/event/map.ts
+@packages-user/data-state/src/event/hero.ts
+@packages-user/data-state/src/event/event.ts
+@packages-user/data-system/src/event/types.ts
+@packages-user/data-system/src/event/system.ts
+@packages-user/data-system/src/event/executor.ts
+@packages-user/data-base/src/map/types.ts
+@packages-user/data-base/src/hero/types.ts
+@packages-user/data-common/src/common/mover.ts
+@packages-user/data-state/src/hero/moverImpl.ts
+
+
+
+
+ Task 1: 固化八个 event built-in 的参数字段与注册语义
+ .planning/phases/03-data-completion/03-EVENT-CONTRACT.md
+ packages-user/data-state/src/event/types.ts, packages-user/data-state/src/event/map.ts, packages-user/data-base/src/map/types.ts, packages-user/data-base/src/hero/types.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-system/src/event/types.ts, .planning/phases/03-data-completion/03-CONTEXT.md
+ 记录并批准八个 event built-in 的确切参数接口、失败边界、临时事件插入语义与 AnonTokyo 注册 seam;记录必须符合 D-24/D-27 的锁定范围
+ 用户已锁定八个名称、`(param, env)`、`IBlockEventEnv`、安全 void 失败以及地图/玩家/事件三组范围;当前源码仍只定义了部分参数,因此实现前必须把剩余字段与注册 seam 写成可执行记录,而不是由实现者补猜。
+
+
+
+
+ 暂停并在 `03-EVENT-CONTRACT.md` 记录用户确认,不实现源码。逐项记录且核对全部八个名称:`eventSetBlock`(设置指定地图图块)、`eventMoveBlock`(动态对象移动后转静态,包含已锁定的可选 safe 判定)、`eventDeleteBlock`(删除指定动态/静态对象)、`eventMoveHero`(按移动序列)、`eventMoveHeroStep`(向前一步)、`eventTouchFront`(触发面前 `onTouch`)、`eventInsertEvents`(临时插入指定事件序列)和 `eventInsertEvent`(临时插入指定事件 id)。记录每项参数字段、`(param, env)`、`IBlockEventEnv`、缺失目标时安全跳过并返回 void、异步等待边界,以及通过现有 `IGameEventInit` 或等价内部 seam 进入 AnonTokyo 的方式。记录不得新增未批准的 legacy 函数或改变上述范围;任一字段仍是 TBD/未确认时停止,不以实现者判断填补 (D-02, D-11, D-17, D-24, D-27)。
+
+ $c = Get-Content -Raw ".planning/phases/03-data-completion/03-EVENT-CONTRACT.md"; $names = @('eventSetBlock','eventMoveBlock','eventDeleteBlock','eventMoveHero','eventMoveHeroStep','eventTouchFront','eventInsertEvents','eventInsertEvent'); foreach ($name in $names) { if ($c -notmatch [regex]::Escape($name)) { exit 1 } }; if ($c -match 'TBD|待确认|未确认|用户确认中') { exit 1 }; foreach ($token in @('param','env','IBlockEventEnv','void','safe','onTouch','注册')) { if ($c -notmatch [regex]::Escape($token)) { exit 1 } }
+ 文件缺失、任一批准名称未出现、任一函数缺少字段/失败/注册结论、记录含 TBD/待确认/未确认/用户确认中,或没有记录 `param`、`env`、`IBlockEventEnv`、safe、onTouch 与 void 语义
+
+
+ - 八个候选名称、每个参数接口字段、失败行为、异步等待边界和注册方式均有用户确认记录 (D-06, D-17, D-27)
+ - 后续实现任务只消费该记录,不自行新增事件函数或字段 (D-11, D-24)
+
+ 回复 confirm-record、edit-record,或直接修改契约文件后回复 approved;记录必须保持 D-24/D-27 的锁定范围
+ 事件公共契约被用户逐项确认并记录,Task 2 可无歧义实现。
+
+
+
+ Task 2: 实现最小 event built-ins、默认注册项与行为测试
+ packages-user/data-state/src/event/types.ts, packages-user/data-state/src/event/map.ts, packages-user/data-state/src/event/hero.ts, packages-user/data-state/src/event/event.ts, packages-user/data-state/src/event/index.ts, packages-user/data-state/src/event/event.test.ts, packages-user/data-system/src/event/system.ts, packages-user/data-state/src/core.ts
+ .planning/phases/03-data-completion/03-EVENT-CONTRACT.md, packages-user/data-state/src/event/types.ts, packages-user/data-state/src/event/map.ts, packages-user/data-state/src/event/hero.ts, packages-user/data-state/src/event/event.ts, packages-user/data-system/src/event/system.ts, packages-user/data-system/src/event/types.ts, packages-user/data-system/src/event/executor.ts, packages-user/data-base/src/map/types.ts, packages-user/data-base/src/hero/types.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-state/src/hero/moverImpl.ts, dev.md
+ 逐字消费 checkpoint 记录实现八个函数:`eventSetBlock`、`eventMoveBlock`、`eventDeleteBlock`、`eventMoveHero`、`eventMoveHeroStep`、`eventTouchFront`、`eventInsertEvents`、`eventInsertEvent`。每个函数拥有独立参数 interface,统一接收 `(param, env)`,所有目标解析优先使用 env.map/env.layer/env.heroFloor,缺少 map/layer/tile/hero/事件 id 时安全 return void;移动、事件链、动态删除等 Promise 必须 await 完整动作 (D-06, D-12, D-24, D-27)。模块提供带稳定名称的默认 `BuiltInFunction` 注册项,顶层按 D-10 装配至 AnonTokyo,不把函数实现复制到 CoreState,也不注册未批准的 legacy catalog。新增 tests 使用 fake/state fixture 和显式依赖注入,逐项验证 map set、dynamic transfer→move→static(safe 分支)、delete、hero sequence/forward/front touch、temporary event sequence/id;每个 `it` 前写中文覆盖注释 (D-01, D-03, D-04, D-10, D-11)。
+
+ pnpm exec vitest run packages-user/data-state/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts
+ 任一测试非零退出、输出 failed、任何函数未安全跳过失败输入、异步动作未完成就返回、未批准函数被注册或 built-in 名称/参数与 checkpoint 记录不一致
+
+
+ - 八个函数全部从 data-state barrel 可消费,注册项与 AnonTokyo built-in lookup 一致 (D-10, D-27)
+ - 地图/玩家/事件三组行为全部有 focused assertions,且失败输入只安全返回 void (D-24, D-27)
+ - Node 环境执行事件函数不读取 DOM、render global 或 legacy global (D-12)
+
+ 最小事件内建函数闭环完成并通过 focused data-side tests,未扩大到完整 legacy 事件目录。
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|---|---|
+| AnonTokyo script parameters → built-ins | event parameters can name invalid tiles, positions, events, or targets |
+| IBlockEventEnv → map/hero mutation | event environment may omit map/layer/tile or refer to an unavailable floor |
+| temporary event insertion → executor | inserted ids/statements can affect later event execution order |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
+|---|---|---|---|---|---|
+| T-03-04 | Tampering | event built-in parameters | medium | mitigate | Validate target existence and use safe no-op returns; reuse map/hero public guards. |
+| T-03-05 | DoS | async hero/event built-ins | medium | mitigate | Await one complete action and avoid unbounded self-insertion; test missing targets and empty sequences. |
+| T-03-SC | Tampering | npm/pip/cargo installs | low | accept | No package installation is planned. |
+
+
+
+- Blocking contract checkpoint is complete before Task 2.
+- `pnpm exec vitest run packages-user/data-state/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts`
+- Existing data suite remains green after event registration changes.
+
+
+
+The user-approved minimal event built-in set is implemented with exact confirmed parameter contracts, awaits long actions, safely skips invalid inputs, and is registered through top-level assembly without expanding legacy scope.
+
+
+
diff --git a/.planning/phases/03-data-completion/03-03-PLAN.md b/.planning/phases/03-data-completion/03-03-PLAN.md
new file mode 100644
index 0000000..318d012
--- /dev/null
+++ b/.planning/phases/03-data-completion/03-03-PLAN.md
@@ -0,0 +1,162 @@
+---
+phase: 03-data-completion
+plan: 03
+type: execute
+wave: 3
+depends_on: [03-01, 03-02]
+files_modified:
+ - .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md
+ - packages-user/data-common/src/replay/func.ts
+ - packages-user/data-state/src/replay/types.ts
+ - packages-user/data-state/src/replay/commands.ts
+ - packages-user/data-state/src/replay/index.ts
+ - packages-user/data-state/src/replay/commands.test.ts
+ - packages-user/data-state/src/core.ts
+ - packages-user/data-state/src/index.ts
+autonomous: false
+requirements: [DATA-01]
+estimate:
+ tokens: 70000
+ raw_tokens: 70000
+ tasks: 3
+ confidence: low
+must_haves:
+ truths:
+ - "Replay safety decoration is applied only to externally callable state-changing entrances that can affect saved state; queries, pure calculations and internal helpers are not decorated (D-05)"
+ - "Async decorated actions preserve collection/replay completion until movement, pathfinding, event chains or combat Promises settle (D-06)"
+ - "The top-level command registry has exactly the approved ordered entries: up, right, down, left, auto-pathfind-to-point, use-item, equip, unequip (D-08, D-25)"
+ - "Command implementations await the complete action and return failure rather than silently advancing when the action cannot execute (D-06, D-07)"
+ artifacts:
+ - path: "packages-user/data-state/src/replay/types.ts"
+ provides: "stable command identifiers and module registration item types"
+ - path: "packages-user/data-state/src/replay/commands.ts"
+ provides: "default command implementations for movement, pathfinding, item and equipment actions"
+ - path: "packages-user/data-state/src/replay/commands.test.ts"
+ provides: "ordered registration and awaited command behavior tests"
+ - path: "packages-user/data-common/src/replay/func.ts"
+ provides: "async-safe replay decorator lifecycle"
+ key_links:
+ - "CoreState top-level registration → ordered command constants → ReplaySystem.registerCommand"
+ - "command execute → existing hero/path/item/equipment APIs → awaited onEnd/result"
+ - "shouldReplay wrapper → collection context remains active through Promise settlement"
+---
+
+
+扩展 replay 闭环:修复/补强现有 replay safety decorator 的异步生命周期,定义模块默认 command registration items,并让顶层按用户锁定顺序注册八个可回放状态入口。先在 checkpoint 完整记录稳定 enum code、注册顺序与 replay 访问边界,避免实现时遗漏任一入口或猜测新的公共属性。
+
+Purpose: D-05/D-06 防止长动作录制过早完成,D-08/D-25 保证录像命令长期稳定且不会由各模块自行占用编号。
+Output: async-safe decorators、ordered command registry、command behavior tests、用户确认记录。
+Decision trace: D-01: closure coverage; D-03: explicit fixtures; D-04: fixed data tests; D-05: replay decoration boundary; D-06: awaited replay actions; D-07: failure result boundary; D-08: stable top-level codes; D-09: factory ownership; D-10: top-level assembly; D-11: minimal event scope; D-17: blocking clarification; D-23: no-options factory; D-25: ordered commands.
+
+
+
+@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
+@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/REQUIREMENTS.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/03-data-completion/03-CONTEXT.md
+@.planning/phases/03-data-completion/03-RESEARCH.md
+@packages-user/data-common/src/replay/types.ts
+@packages-user/data-common/src/replay/system.ts
+@packages-user/data-common/src/replay/sandbox.ts
+@packages-user/data-common/src/replay/func.ts
+@packages-user/data-common/src/common/mover.ts
+@packages-user/data-system/src/path/system.ts
+@packages-user/data-state/src/core.ts
+@packages-user/data-base/src/hero/types.ts
+
+
+
+
+ Task 1: 固化 replay 数值码与 top-level replay 访问边界
+ .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md
+ packages-user/data-common/src/replay/types.ts, packages-user/data-common/src/replay/system.ts, packages-user/data-state/src/core.ts, .planning/phases/03-data-completion/03-CONTEXT.md
+ 记录并批准八个 replay command 的稳定 top-level enum code、D-25 注册顺序与 CoreState/Node runner 访问入口
+ 用户已锁定命令顺序、top-level stable enum ownership 与失败策略;实现前仍须逐项记录 enum code 名称/值、访问边界及 pathfinding 等待点,不能遗漏或自行增加公共访问面。
+
+
+
+
+ 暂停并在 `03-REPLAY-CONTRACT.md` 逐项记录八个稳定 top-level enum code 与顺序:up、right、down、left、auto-pathfind-to-point、use-item、equip、unequip;同时记录 CoreState/Node runner 的既有或内部访问 seam,以及 auto-pathfind 对现有 `PathfindingSystem.moveTo`/controller.onEnd 的等待边界。不要把 Plan 01 的 private direct tracer harness 当作最终 registry,不得自行增加 `ICoreState` 公共成员、重排命令、改用不稳定字符串 code 或凭空分配另一套 command ownership (D-08, D-09, D-17, D-23, D-25)。
+
+ $c = Get-Content -Raw ".planning/phases/03-data-completion/03-REPLAY-CONTRACT.md"; $commands = @('up','right','down','left','auto-pathfind','use-item','equip','unequip'); foreach ($command in $commands) { if ($c -notmatch [regex]::Escape($command)) { exit 1 } }; if ($c -notmatch 'up[\s\S]*right[\s\S]*down[\s\S]*left[\s\S]*auto-pathfind[\s\S]*use-item[\s\S]*equip[\s\S]*unequip') { exit 1 }; foreach ($token in @('enum','stable','CoreState','Node','runner','access','registry','controller.onEnd')) { if ($c -notmatch [regex]::Escape($token)) { exit 1 } }; if ($c -match 'TBD|待确认|未确认|用户确认中') { exit 1 }
+ 文件缺失、八个命令名称/顺序任一项没有逐项记录、稳定 enum code、CoreState/Node runner access 或 top-level registry 任一项缺失、controller.onEnd 等待边界缺失,或记录仍包含 TBD/待确认/未确认/用户确认中
+
+
+ - 八个 command 的稳定 top-level enum code、注册顺序、CoreState/runner 访问方式和 pathfinding 等待边界均有完整确认记录 (D-08, D-25)
+
+ 回复 confirm-record、edit-record,或直接修改契约文件后回复 approved
+ Replay command 公共边界已确认,后续实现不需要猜测稳定编号或访问入口。
+
+
+
+ Task 2: async-safe replay decorators 与模块默认 command items
+ packages-user/data-common/src/replay/func.ts, packages-user/data-state/src/replay/types.ts, packages-user/data-state/src/replay/commands.ts, packages-user/data-state/src/replay/index.ts, packages-user/data-state/src/replay/commands.test.ts
+ .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md, packages-user/data-common/src/replay/func.ts, packages-user/data-common/src/replay/types.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-system/src/path/system.ts, packages-user/data-base/src/hero/types.ts, packages-user/data-state/src/core.ts, dev.md
+ 按 checkpoint 记录实现 replay types/command item,不让子系统直接分配全局 code。定义 top-level stable enum code 与默认 registration items;命令实现使用现有 API:四向移动等待 mover controller.onEnd,自动寻路等待 `PathfindingSystem` controller.onEnd,使用道具调用 `hero.items.useItem`,装备调用 `hero.equip.equip`,卸下调用 `hero.equip.unequip`。对参数无效、目标不存在、已有移动或动作失败返回 false,不吞掉状态失败;首分歧的 thrown diagnostic 由 Plan 04 的 Node verifier 负责,不改变现有 replay boolean 公共接口 (D-06, D-07, D-08, D-25)。审计 `shouldReplay`/`ignoreReplay`:只包装外部状态改变入口;Promise 返回值必须在完整动作结束后恢复收集上下文,纯查询/计算/内部 helper 不加 decorator。测试 async deferred action、nested collection、duplicate code rejection、八项顺序和每项参数/返回值,使用显式 fake/state fixture;新增或修改的每个 `it` 调用前必须有一行中文注释说明当前覆盖内容,遵守 dev.md:85 (D-01, D-03, D-05, D-06)。
+
+ pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts
+ 测试非零退出、任何异步 action 在 Promise settle 前完成、命令顺序/code 与 checkpoint 不同、重复注册被接受、纯查询被记录或失败动作返回 true
+
+
+ - decorator 对同步/异步外部状态入口的 collection 生命周期均正确,内部/纯函数没有重复包装 (D-05, D-06)
+ - 八个 registration items 均存在且只描述各自模块动作,未在模块内抢占其他模块 code (D-08)
+ - command tests 全部通过,参数失败可观察为 false (D-07)
+
+ Replay decorator 与八个模块默认 command items 已有可执行、可测试的实现。
+
+
+
+ Task 3: CoreState 顶层注册八个稳定 replay commands
+ packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts, packages-user/data-state/src/replay/commands.test.ts
+ .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md, packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts, packages-user/data-state/src/event/index.ts, packages-user/data-state/src/replay/types.ts, packages-user/data-state/src/replay/commands.ts, packages-user/data-state/src/replay/index.ts, packages-user/data-common/src/replay/system.ts, packages-user/data-system/src/path/system.ts
+ 在 CoreState 的最终顶层装配区创建/持有经 checkpoint 确认的 ReplaySystem,并按“up/right/down/left/auto-pathfind/use-item/equip/unequip”顺序一次性注册所有 module defaults;top-level stable enum 是唯一 code owner,不让 data-common/data-system 子模块自行注册全局编号。将现有 hero mover 与 Phase 2 L2 pathfinder 以合法数据层方向绑定,auto-pathfind 使用确认的 target 参数并等待完整 controller。更新 data-state 根 barrel,保留现有浏览器兼容 singleton,同时导出无参数 factory 以及经 Plan 02/03 确认的 event/replay registration items,避免低层模块重新依赖该 barrel。新增装配回归断言读取每一个稳定 code 的顺序、访问方式和所有命令存在;不添加选项型 factory API,不改变 Phase 4 render click boundary;所有新增或修改的 `it` 调用前必须有一行中文注释说明当前覆盖内容,遵守 dev.md:85 (D-08, D-09, D-10, D-11, D-23, D-25)。
+
+ pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts; if ($?) { pnpm exec vitest run packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state }
+ 任一命令非零退出、注册顺序不匹配、CoreState 没有独立 ReplaySystem、或数据端既有测试出现 failed
+
+
+ - fresh CoreState 每次拥有独立 replay system,八个 command 按确认顺序注册 (D-09, D-25)
+ - top-level 是唯一稳定 code 分配点,module items 只提供实现 (D-08, D-10)
+ - full focused data suite 仍通过 (D-04)
+
+ CoreState 能直接提供经确认的八项 replay command registry,所有状态改变动作具备完整等待语义。
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|---|---|
+| replay route bytes → command dispatcher | route supplies numeric code and encoded parameters |
+| external state action → replay recording | action may be asynchronous or fail after starting |
+| module registration item → top-level registry | modules could accidentally collide or reorder stable codes |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
+|---|---|---|---|---|---|
+| T-03-06 | Tampering | unknown/duplicate replay code | medium | mitigate | Central registration rejects duplicates; runner reports the exact index/code/params/reason. |
+| T-03-07 | DoS | long async movement/event action | medium | mitigate | Await controller/event/combat Promise before step completion and stop on false. |
+| T-03-SC | Tampering | npm/pip/cargo installs | low | accept | No package installation is planned. |
+
+
+
+- Blocking replay contract checkpoint is complete before implementation.
+- `pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts`
+- Focused four-package suite remains green.
+
+
+
+All approved state-changing replay entrances are decorated and registered exactly once at the top level in the confirmed stable order, with complete async completion semantics and explicit failure results.
+
+
+
diff --git a/.planning/phases/03-data-completion/03-04-PLAN.md b/.planning/phases/03-data-completion/03-04-PLAN.md
new file mode 100644
index 0000000..09602b8
--- /dev/null
+++ b/.planning/phases/03-data-completion/03-04-PLAN.md
@@ -0,0 +1,168 @@
+---
+phase: 03-data-completion
+plan: 04
+type: execute
+wave: 4
+depends_on: [03-01, 03-02, 03-03, 03-06]
+files_modified:
+ - packages-user/data-state/test/fixtures/closed-loop.ts
+ - script/test-data-node.ts
+ - package.json
+ - packages-user/data-state/test/coreNode.test.ts
+ - packages-user/data-state/test/nodeReplay.test.ts
+ - .planning/phases/03-data-completion/03-REPLAY-DIAGNOSTICS.md
+autonomous: false
+requirements: [DATA-01]
+estimate:
+ tokens: 60000
+ raw_tokens: 60000
+ tasks: 3
+ confidence: low
+must_haves:
+ truths:
+ - "固定 fixture 覆盖顶层初始化、至少一个玩家 replay command、事件/状态变化与正常播放结束 (D-13)"
+ - "Node replay 失败立即停止并抛出 verifier-local error,报告首个分歧的 command index、top-level enum code、params 和 reason,不改变 replay public interface (D-07)"
+ - "最终验收只在 replay 正常结束后精确比较 hero 全部属性和所有地图矩阵;逐步 hero checks 仅作为可选单测诊断 (D-14, D-26)"
+ - "`pnpm test:data-node` 是独立于 Vitest 的 Node 进程门禁,失败退出非零 (D-15)"
+ artifacts:
+ - path: "packages-user/data-state/test/fixtures/closed-loop.ts"
+ provides: "固定 replay route、初始 reset、预期 hero full attributes 与 all map matrices"
+ - path: "script/test-data-node.ts"
+ provides: "首分歧诊断与最终 snapshot 比较"
+ - path: "packages-user/data-state/test/nodeReplay.test.ts"
+ provides: "成功、unknown command、false command、snapshot mismatch regressions"
+ - path: ".planning/phases/03-data-completion/03-REPLAY-DIAGNOSTICS.md"
+ provides: "已批准的首分歧 thrown diagnostic 与输出字段记录"
+ key_links:
+ - "fixed fixture → createCoreState() → explicit initial-state load → CoreState replay registry"
+ - "ReplaySandbox step result → route index/code/params diagnostic → process exit code"
+ - "normal end → hero.attribute.toStructured() + every map/layer getMapData() exact comparison"
+---
+
+
+把 tracer 扩展为阶段最终固定录像验收:复用经确认的 factory、event built-ins 与八项 replay registry,使用固定 fixture 运行独立 Node verifier,在结束时比较 hero 全属性和所有地图矩阵,并覆盖首个 replay 分歧的 thrown diagnostic。实现前先用 blocking checkpoint 记录并批准 D-07 的错误机制与输出字段,避免 verifier 自行发明新的公共错误契约。
+
+Purpose: 让 DATA-01 的 Node 独立回放验证可重复、可定位,而不把逐步地图快照或完整核心覆盖提前拉入 Phase 3。
+Output: 完整固定 fixture、Node verifier、focused Node runner tests。
+Decision trace: D-01: closure coverage; D-03: explicit fixtures; D-04: fixed data tests; D-06: awaited actions; D-07: first-divergence stop; D-08: stable command codes; D-09: factory entry; D-11: minimal event scope; D-12: Node boundary; D-13: fixed replay; D-14: exact end snapshots; D-15: dedicated Node process; D-17: blocking clarification; D-21: dedicated `pnpm test:data-node` fixture runner; D-23: no-options factory; D-24: approved event path; D-25: ordered commands; D-26: end-only comparison; D-27: event signature; D-28: Node-safe constructor.
+
+
+
+@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
+@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/REQUIREMENTS.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/03-data-completion/03-CONTEXT.md
+@.planning/phases/03-data-completion/03-01-SUMMARY.md
+@.planning/phases/03-data-completion/03-02-SUMMARY.md
+@.planning/phases/03-data-completion/03-03-SUMMARY.md
+@.planning/phases/03-data-completion/03-REPLAY-DIAGNOSTICS.md
+@packages-user/data-common/src/replay/types.ts
+@packages-user/data-common/src/replay/system.ts
+@packages-user/data-common/src/replay/sandbox.ts
+@packages-user/data-state/src/core.ts
+@packages-user/data-state/src/replay/types.ts
+@packages-user/data-state/src/replay/commands.ts
+@packages-user/data-state/src/event/types.ts
+@packages-user/data-state/src/event/map.ts
+@packages-user/data-base/src/map/types.ts
+@packages-user/data-base/src/hero/types.ts
+
+
+
+
+ Task 1: 批准首分歧 thrown diagnostic 与输出字段
+ .planning/phases/03-data-completion/03-REPLAY-DIAGNOSTICS.md
+ .planning/phases/03-data-completion/03-CONTEXT.md, .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md, packages-user/data-common/src/replay/types.ts, packages-user/data-common/src/replay/sandbox.ts, packages-user/data-state/src/replay/types.ts
+ 批准 Node verifier 在首个 replay 分歧立即抛出 implementation-local error,并在本地 diagnostic/output contract 中报告 index、top-level stable enum code、params、reason;不新增 user-facing error class,不改变 replay command 的 Promise<boolean> 公共接口
+ D-07 与最新用户澄清已经锁定失败机制和四个报告字段;本 checkpoint 只把它们写成可测试的 verifier-local 记录,并确认参数展示使用确定性的安全表示,不把格式扩展成新的 replay API。
+
+
+
+
+ 暂停并在 `03-REPLAY-DIAGNOSTICS.md` 记录批准结果:首个 unknown command、false result、command throw 或最终 snapshot mismatch 均在首个分歧处停止;verifier-local thrown error/output 必须包含 command index、Plan 03 top-level stable enum code(未知 code 也原样报告)、原始 params 与可读 reason。参数只做确定性的安全展示,不改变 route 编码;不新增 replay public result/error interface,也不要求固定的公共 error class。若记录缺少任一字段或出现与 D-07 冲突的 TBD/待确认内容则不能继续 (D-02, D-07, D-14, D-15, D-26, D-27, D-28)。
+
+ $c = Get-Content -Raw ".planning/phases/03-data-completion/03-REPLAY-DIAGNOSTICS.md"; foreach ($token in @('throw','index','code','params','reason','stable','enum','implementation-local')) { if ($c -notmatch [regex]::Escape($token)) { exit 1 } }; $promiseSyntax = 'Promise' + [char]60 + 'boolean' + [char]62; $promiseEntity = 'Promise' + [char]38 + 'lt;boolean' + [char]38 + 'gt;'; if ($c -notmatch [regex]::Escape($promiseSyntax) -and $c -notmatch [regex]::Escape($promiseEntity)) { exit 1 }; if ($c -match 'TBD|待确认|未确认|用户确认中|logger-only|result-object') { exit 1 }
+ 记录缺失 thrown mechanism、index/code/params/reason 任一字段、top-level stable enum 约束或 public Promise<boolean> 保持语义,或仍含 TBD/待确认/未确认/用户确认中
+
+
+ - 首分歧机制明确为 verifier-local thrown error,且立即停止 (D-07)
+ - 诊断字段完整包含 index、stable enum code、params、reason,不改变 replay public interface (D-07, D-08)
+
+ 回复 approve-locked-contract、edit-record,或直接修改记录后回复 approved;不得修改 D-07 的 thrown/first-divergence/四字段锁定语义
+ 首分歧错误机制与本地输出字段已完成 blocking approval,Task 2 才可实现 verifier。
+
+
+
+ Task 2: 固定闭环 fixture 与首分歧 Node verifier
+ packages-user/data-state/test/fixtures/closed-loop.ts, script/test-data-node.ts, package.json
+ .planning/phases/03-data-completion/03-REPLAY-DIAGNOSTICS.md, packages-user/data-state/test/fixtures/closed-loop.ts, script/test-data-node.ts, packages-user/data-common/src/replay/types.ts, packages-user/data-common/src/replay/system.ts, packages-user/data-common/src/replay/sandbox.ts, packages-user/data-state/src/core.ts, packages-user/data-state/src/replay/commands.ts, packages-user/data-state/src/event/types.ts, packages-user/data-base/src/map/types.ts, packages-user/data-base/src/hero/types.ts, .planning/phases/03-data-completion/03-CONTEXT.md
+ 在 Plan 03-01 的 tracer fixture 与 Node-safe factory 之上,将 fixture 固定为可重复的 in-memory 数据:显式 tile/map/event definitions、初始 hero/flags/maps/enemy save state、ReplayArray route、expected hero full attribute object 和每个楼层每个 layer 的完整 Uint32Array matrix。route 至少包含一个 Plan 03 批准的玩家动作,并经过 event built-in 造成状态变化后正常结束 (D-13, D-24, D-25)。新增 `pnpm test:data-node` 到 package.json,runner 直接从 `packages-user/data-state/src/core.ts` 导入 `createCoreState()` 而非兼容 singleton;每步先读取 route step;若 command 不存在、command 返回 false、执行抛错或最终 snapshot 不一致,按已批准的 `03-REPLAY-DIAGNOSTICS.md` 在首个分歧处抛出 verifier-local error,并报告 index/code/params/reason、返回非零;不要改变现有 IReplayCommand boolean 公共接口来承载额外诊断 (D-07, D-15)。只有 route 全部成功且 sandbox ended 后,才比较 hero 的完整 `toStructured()` 与所有 maps/layers 的 `getMapData()`,不在最终 runner 中加入逐步地图比较 (D-14, D-26)。
+
+
+ pnpm test:data-node
+ 命令非零退出、fixture 依赖随机/legacy/browser 数据、失败未在首个分歧抛错并停止、诊断缺少 index/code/params/reason、改变 replay public boolean contract、或最终比较发生在正常结束之前
+
+
+ - 固定 fixture 在重复运行中产生相同 route、hero full attributes 和全部 map matrices (D-13, D-14)
+ - unknown/false/throw/snapshot mismatch 均以首个分歧 thrown diagnostic 和非零状态结束 (D-07, D-15)
+ - Node runner 不导入会创建兼容 singleton 的浏览器入口 (D-12, D-28)
+
+ 固定闭环 Node verifier 可独立、确定性地成功或首分歧失败。
+
+
+
+ Task 3: Node replay verifier regression tests
+ packages-user/data-state/test/nodeReplay.test.ts, packages-user/data-state/test/coreNode.test.ts
+ .planning/phases/03-data-completion/03-REPLAY-DIAGNOSTICS.md, script/test-data-node.ts, packages-user/data-state/test/fixtures/closed-loop.ts, packages-user/data-state/src/core.ts, packages-user/data-common/src/replay/types.ts, packages-user/data-common/src/replay/system.ts, packages-user/data-common/src/replay/sandbox.ts, dev.md
+ 为 verifier 提供可注入 fixture/route/expected snapshot 的测试 harness,覆盖成功结束、未知 code、command 返回 false、command 抛错、hero snapshot mismatch 和 map matrix mismatch;断言 verifier-local thrown error/output 包含首个 index、top-level stable code、完整 params 与 reason,且后续 route command 未执行。补充 Plan 03-01 的 factory 独立性回归,确认两个无参数 `createCoreState()` 实例不共享 hero、maps、eventStore、save state 可变引用,且 Node 路径不触发浏览器初始化。测试使用显式 fake,不以 shell 全局 stub 伪造 Node host;每个 `it` 调用前必须有一行中文注释说明覆盖内容 (D-01, D-03, D-04, D-07, D-14, D-15, D-23, D-26, D-28)。
+
+ pnpm exec vitest run packages-user/data-state/test/nodeReplay.test.ts packages-user/data-state/test/coreNode.test.ts
+ 测试非零退出、thrown diagnostic 缺少任一首分歧字段、失败后仍执行后续 command、最终 snapshot 比较被跳过或测试依赖 DOM/legacy global
+
+
+ - success/failure branches deterministic and isolated
+ - failure diagnostics are exact enough for `pnpm test:data-node` triage (D-07)
+ - final acceptance semantics remain end-only for hero/map snapshots (D-14, D-26)
+
+ Node verifier 的成功、首分歧和最终快照语义均有自动回归覆盖。
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|---|---|
+| fixture route → Node command execution | static fixture bytes/params drive state-changing APIs |
+| replay result → process exit | false/throw/snapshot mismatch determines CI success |
+| map/hero live state → expected snapshot | mutable runtime state is compared against trusted fixture output |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
+|---|---|---|---|---|---|
+| T-03-08 | Tampering | malformed route command | medium | mitigate | Validate command lookup and stop before executing any later route step. |
+| T-03-09 | Repudiation | ambiguous replay failure | medium | mitigate | Emit deterministic index/code/params/reason and return non-zero. |
+| T-03-10 | Tampering | snapshot comparison | low | mitigate | Compare structured hero attributes and copied Uint32Array matrices only after normal end. |
+| T-03-SC | Tampering | npm/pip/cargo installs | low | accept | No package installation is planned. |
+
+
+
+- `pnpm exec vitest run packages-user/data-state/test/nodeReplay.test.ts`
+- `pnpm test:data-node`
+- Repeat `pnpm test:data-node` twice and compare exit status/output snapshot.
+
+
+
+The fixed replay fixture exercises the approved data-side closed loop and the dedicated Node command provides deterministic end-only acceptance with actionable first-divergence failures.
+
+
+
diff --git a/.planning/phases/03-data-completion/03-05-PLAN.md b/.planning/phases/03-data-completion/03-05-PLAN.md
new file mode 100644
index 0000000..811acac
--- /dev/null
+++ b/.planning/phases/03-data-completion/03-05-PLAN.md
@@ -0,0 +1,166 @@
+---
+phase: 03-data-completion
+plan: 05
+type: execute
+wave: 5
+depends_on: [03-04, 03-06]
+files_modified:
+ - .planning/phases/03-data-completion/03-COMMON-CYCLE-CONTRACT.md
+ - packages/common/src/utils/types.ts
+ - packages-user/data-state/test/dataClosure.test.ts
+ - script/check-data-type.ts
+ - script/check-data-circular.ts
+autonomous: false
+requirements: [DATA-01]
+estimate:
+ tokens: 65000
+ raw_tokens: 65000
+ tasks: 3
+ confidence: low
+must_haves:
+ truths:
+ - "四个数据包及其相互边界没有本阶段允许范围内的 TypeScript 错误;render 与明确 legacy-only 的既有诊断不被扩大为本阶段功能 (D-16)"
+ - "四个数据包内部及互相边界没有循环引用;`@motajs/common` 只能作为已经无环的基础依赖,common/data-common back-edge 不能被 gate 静默放行 (D-16, D-20)"
+ - "DATA-01 的闭环覆盖明确验证 enemy、Flag、combat、save/load、trigger/event 与 replay 系统;完整 Phase 6 边界覆盖仍然延期 (D-01)"
+ - "完整数据端 Vitest suite、Node verifier、type gate 和 circular gate 均为非 watch、可重复命令 (D-04, D-15, D-16)"
+ artifacts:
+ - path: "script/check-data-type.ts"
+ provides: "只报告 data-common/base/system/state 计划范围诊断的类型门禁"
+ - path: "script/check-data-circular.ts"
+ provides: "四个数据包及 transitive common/data-common boundary 的循环检查"
+ - path: "packages/common/src/utils/types.ts"
+ provides: "不再从 common/utils/types.ts 反向导入 data-common 的低层公共类型"
+ - path: "packages-user/data-state/test/dataClosure.test.ts"
+ provides: "DATA-01 enemy/Flag/combat/save-load/trigger-event/replay focused closure coverage"
+ key_links:
+ - "check-data-type → vue-tsc output classification → non-zero only for in-scope diagnostics"
+ - "check-data-circular → madge graph → four-package and common/data-common boundary cycles"
+ - "CoreState imports → direct lower-level modules → no enemy/legacy barrel back-edge"
+---
+
+
+完成 Phase 3 质量闭环:修复本阶段数据包边界内的实际类型/循环问题,提供可重复的四包 type/circular gate,并运行完整数据端测试与独立 Node 验收。仅做使数据端可用所需的 import/类型/循环收口,不进行完整 legacy 移除、渲染适配或 Phase 6 全覆盖。
+
+Purpose: 让 DATA-01 的“可用且可在 Node 独立回放”具有明确、可审计的质量门禁。
+Output: type/circular checker scripts、必要的低层导入修复、最终验证证据。
+Decision trace: D-01: closure coverage; D-02: stop on undefined contracts; D-03: explicit fixtures; D-04: fixed data tests; D-05: replay decoration boundary; D-06: awaited actions; D-07: first-divergence behavior; D-08: stable command ownership; D-11: minimal event scope; D-14: exact snapshots; D-15: dedicated Node process; D-16: four-package quality gates; D-17: blocking clarification; D-20: cycle closure; D-23: no-options factory; D-24: approved event coverage; D-25: ordered commands; D-26: end-only comparison; D-28: Node-safe path.
+
+
+
+@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
+@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/REQUIREMENTS.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/03-data-completion/03-CONTEXT.md
+@.planning/phases/03-data-completion/03-RESEARCH.md
+@.planning/phases/03-data-completion/03-04-SUMMARY.md
+@packages/common/src/utils/types.ts
+@packages-user/data-state/src/core.ts
+@packages-user/data-state/src/enemy/index.ts
+@packages-user/data-state/src/legacy/index.ts
+@packages-user/data-state/src/legacy/move.ts
+@packages-user/data-state/src/ins.ts
+@packages-user/data-common/src/index.ts
+@packages-user/data-base/src/index.ts
+@packages-user/data-system/src/index.ts
+@packages-user/data-state/src/index.ts
+@package.json
+@dev.md
+
+
+
+
+ Task 1: 批准 common/data-common back-edge 的最小闭环契约
+ .planning/phases/03-data-completion/03-COMMON-CYCLE-CONTRACT.md
+ packages/common/src/utils/types.ts, packages-user/data-common/src/common/types.ts, packages-user/data-common/src/index.ts, packages-user/data-common/src/types.ts, .planning/phases/03-data-completion/03-CONTEXT.md, .planning/phases/03-data-completion/03-RESEARCH.md, dev.md
+ 在不改变四层用户-owned data contract 的前提下,批准移除 `packages/common/src/utils/types.ts:1` 对 `@user/data-common` 的反向 import,并记录 `IFacedTileLocator.direction` 的兼容表示、受影响路径与 gate 判定;不得以“允许 common 基础依赖”为理由保留当前 common/data-common cycles
+ 最新 circular 输出明确有 13 条 cycle 经过 `packages/common/src/utils/types.ts:1` 与 data-common。D-20 只允许依赖已经无环的 `@motajs/common`,因此需要一个显式的低层公共类型边界决定;这是现有 common 公共接口的修复,不能由执行者无 checkpoint 猜测。
+
+
+
+ 暂停并在 `03-COMMON-CYCLE-CONTRACT.md` 写明用户批准的唯一选项、精确修改路径、兼容性约束和 circular gate 规则。记录必须明确:当前 13 条 common/data-common cycles 全部属于失败项;`@motajs/common` 自身也必须先证明为无环基础依赖,不能把 common-only cycle 当作允许项;只有不在本阶段四包/transitive-common 图内的 render/legacy-only 图才可在 D-20 范围外保留。未得到批准前不得修改 `packages/common/src/utils/types.ts` 或任何 data-common public type。
+
+ $c = Get-Content -Raw ".planning/phases/03-data-completion/03-COMMON-CYCLE-CONTRACT.md"; foreach ($token in @('packages/common/src/utils/types.ts','data-common','IFacedTileLocator','13','D-20','失败','批准')) { if ($c -notmatch [regex]::Escape($token)) { exit 1 } }; if ($c -match 'TBD|待确认|未确认|用户确认中') { exit 1 }
+ 契约记录缺失精确 common back-edge 路径、13 条 cycle 的失败判定、兼容表示、D-20 约束或用户批准结果,或仍含未决占位语义
+
+ 回复 numeric-compatible-field,或直接修改契约文件后回复 approved;不得批准保留当前 common/data-common cycles
+ common/data-common cycle closure 的公共边界与精确修改路径已完成 blocking approval。
+
+
+
+ Task 2: 闭合 common/data-common 与剩余数据包循环并建立 scoped gates
+ packages/common/src/utils/types.ts, script/check-data-circular.ts, script/check-data-type.ts
+ .planning/phases/03-data-completion/03-COMMON-CYCLE-CONTRACT.md, packages/common/src/utils/types.ts, packages-user/data-common/src/common/types.ts, packages-user/data-common/src/index.ts, packages-user/data-base/src/index.ts, packages-user/data-system/src/index.ts, packages-user/data-state/src/index.ts, packages-user/data-state/src/enemy/calculator.ts, packages-user/data-state/src/legacy/move.ts, packages-user/data-state/src/ins.ts, .planning/phases/03-data-completion/03-CONTEXT.md, .planning/phases/03-data-completion/03-RESEARCH.md
+ 严格按已批准的 `03-COMMON-CYCLE-CONTRACT.md` 实施 numeric-compatible closure:只移除 `packages/common/src/utils/types.ts:1` 的 data-common back-edge,并保持 data-common 用户接口与导出路径不变,不创建未批准的公共 API。更新 `check-data-circular.ts` 以四个 data package entry 为根,遍历其 transitive `@motajs/common` 图,并将四包内部/互边界 cycle、任何 common/data-common cycle 以及 `@motajs/common` 自身 cycle 作为失败;只有不在本阶段四包/transitive-common 图内的 render/legacy-only 图记录为范围外。保留并明确检查 Plan 03-01 已闭合的 `enemy/calculator.ts → ins` 与 `legacy/move.ts → data-state index` 路径。新增 `check-data-type.ts` 按四包路径分类诊断,不能把 in-scope errors 静默归入 render/legacy-only;不得修改 data-common public interfaces、不得新增依赖、不得把 cycle gate 缩小成只检查四个 index 文件 (D-02, D-16, D-20, D-28)。
+
+ pnpm exec tsx script/check-data-circular.ts; if ($?) { pnpm exec tsx script/check-data-type.ts }
+ common/data-common back-edge 或任一四包内部/互边界 cycle 被漏报、Plan 03-01 的两条 singleton/barrel back-edge 重新出现、四包类型诊断被错误归类,或任一 scoped checker 非零
+
+
+ - 13 条经 `packages/common/src/utils/types.ts:1` 的 common/data-common cycle 已逐条闭合,且没有新增公共 contract 未经 checkpoint 批准 (D-20)
+ - 四包 scoped circular gate 检查 transitive common boundary,四包/common/data-common 及 common 基础包均为零 cycle 才能通过;不把当前 cycle 静默当作允许的 common base dependency (D-16, D-20)
+ - scoped type gate 明确区分四包 diagnostics 与范围外 render/legacy-only diagnostics (D-16)
+
+ common/data-common back-edge、四包 mutual-boundary cycles 与 Wave 1 singleton/barrel back-edges 均有明确闭环实现和可重复 gate。
+
+
+
+ Task 3: 补齐 DATA-01 focused closure 并执行最终门禁
+ packages-user/data-state/test/dataClosure.test.ts
+ packages-user/data-base/src/enemy/types.ts, packages-user/data-base/src/flag/types.ts, packages-user/data-system/src/combat/types.ts, packages-user/data-system/src/event/types.ts, packages-user/data-common/src/save/types.ts, packages-user/data-common/src/replay/types.ts, packages-user/data-state/src/core.ts, script/check-data-type.ts, script/check-data-circular.ts, script/test-data-node.ts
+ 使用显式 fake/state fixture 和现有公开接口,分别提供 focused assertions:enemy manager 的创建/属性或保存行为,Flag 的设置/读取与 save/load,combat 的确定性伤害结果,saveable content 的 saveState/loadState round trip,trigger/event executor 与批准 built-in 的状态变化,以及 replay registry/route 的稳定顺序、异步完成和失败停止;每个 `it` 前写中文覆盖注释。测试覆盖只闭合 DATA-01 当前回放依赖,不替代 Phase 6 的完整边界回归 (D-01, D-02, D-03, D-04, D-05, D-06, D-07, D-08, D-16, D-20, D-23, D-24, D-25, D-26, D-27, D-28)。随后按固定顺序运行 data suite、`pnpm test:data-node`、scoped type/circular gates,并将 `script/test-data-node.ts` 纳入最终 ESLint 与 Prettier 检查;不得把 render/legacy-only 诊断伪装成全仓清洁。
+
+ pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state; if ($?) { pnpm test:data-node }; if ($?) { pnpm exec tsx script/check-data-type.ts }; if ($?) { pnpm exec tsx script/check-data-circular.ts }; if ($?) { pnpm exec eslint packages/common/src/logger.ts packages/common/src/utils/types.ts packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state script/check-data-type.ts script/check-data-circular.ts script/test-data-node.ts }; if ($?) { pnpm exec prettier --check packages/common/src/logger.ts packages/common/src/utils/types.ts packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state script/check-data-type.ts script/check-data-circular.ts script/test-data-node.ts }
+ 任一数据 suite、Node verifier、scoped type/circular gate、focused ESLint 或 scoped Prettier 命令非零;common/data-common 或四包 cycle 被漏报;或 focused closure 没有覆盖 enemy、Flag、combat、save/load、trigger/event、replay 六类行为
+
+
+ - `dataClosure.test.ts` 有可执行且非空的 enemy、Flag、combat、save/load、trigger/event、replay assertions (D-01, DATA-01)
+ - 四包 type gate 与 transitive mutual-boundary circular gate 均可由固定非 watch 命令重复执行并报告零 in-scope failure (D-16, D-20)
+ - final data suite 与 `pnpm test:data-node` 同时通过,且 render/legacy-only 诊断没有被伪装成全仓清洁 (D-04, D-15)
+ - ESLint 与 Prettier 最终检查同时覆盖 `script/test-data-node.ts`,失败时使 Task 3 失败
+ - 无新依赖、无 Phase 4/5/6 文件或行为扩张 (D-01, D-11)
+
+ Phase 3 的数据测试、Node replay、四包类型和包含 common/data-common 边界的循环门禁均有可重复绿色证据。
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|---|---|
+| compiler output → scope classifier | diagnostics must be classified without hiding in-scope failures |
+| madge graph → cycle policy | graph results determine whether data-layer boundaries are safe |
+| final gates → release claim | green commands are the evidence for DATA-01 completion |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
+|---|---|---|---|---|---|
+| T-03-11 | Tampering | type/circular scope scripts | high | mitigate | Fail closed for data-package diagnostics/cycles and print classification; allow only explicitly outside-scope render/legacy-only items. |
+| T-03-12 | Repudiation | final verification evidence | low | mitigate | Run fixed non-watch commands and preserve exact command output in phase summary. |
+| T-03-15 | Tampering | `@motajs/common` ↔ `@user/data-common` import boundary | high | mitigate | Require the approved common-side back-edge removal, traverse the transitive common graph, and fail on every common/data-common or four-package cycle. |
+| T-03-SC | Tampering | npm/pip/cargo installs | low | accept | No package installation is planned. |
+
+
+
+- `pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state`
+- `pnpm test:data-node`
+- `pnpm exec tsx script/check-data-type.ts`
+- `pnpm exec tsx script/check-data-circular.ts`
+- common/data-common back-edge contract checkpoint is approved and its tracked path is absent from the final import graph
+- focused ESLint/Prettier check on all Phase 3 implementation/test/script files
+
+
+
+DATA-01 is supported by a passing data-side suite with focused enemy/Flag/combat/save-load/trigger-event/replay closure, a deterministic Node replay process, and explicit type/circular gates for the four data packages and their mutual boundaries, without pulling render integration, complete legacy migration, or Phase 6 coverage into scope.
+
+
+
diff --git a/.planning/phases/03-data-completion/03-06-PLAN.md b/.planning/phases/03-data-completion/03-06-PLAN.md
new file mode 100644
index 0000000..22f72ed
--- /dev/null
+++ b/.planning/phases/03-data-completion/03-06-PLAN.md
@@ -0,0 +1,128 @@
+---
+phase: 03-data-completion
+plan: 06
+type: execute
+wave: 2
+depends_on: [03-01]
+files_modified:
+ - packages-user/data-common/src/store/tileStore.ts
+ - packages-user/data-common/src/store/tileStore.test.ts
+ - packages-user/data-state/src/legacy/tile.ts
+ - packages-user/data-state/test/tileLegacy.test.ts
+autonomous: true
+requirements: [DATA-01]
+estimate:
+ tokens: 28000
+ raw_tokens: 28000
+ tasks: 2
+ confidence: low
+must_haves:
+ truths:
+ - "Tile runtime implementation consumes the user-owned `ITileRawData.events` map and exposes it through `ITileStore.getEvent(num)` without restoring scalar trigger semantics (D-22)"
+ - "Legacy tile conversion produces the same events-map contract and does not make Node fixture code depend on a legacy trigger scalar (D-03, D-12, D-22)"
+ - "Focused Tile and legacy conversion tests use explicit data and remain separate from Phase 5 full legacy migration (D-01, D-04)"
+ artifacts:
+ - path: "packages-user/data-common/src/store/tileStore.ts"
+ provides: "ITileRawData.events storage and getEvent(num) lookup"
+ - path: "packages-user/data-common/src/store/tileStore.test.ts"
+ provides: "events-map accessor and tile replacement regression coverage"
+ - path: "packages-user/data-state/src/legacy/tile.ts"
+ provides: "legacy-to-events-map conversion"
+ - path: "packages-user/data-state/test/tileLegacy.test.ts"
+ provides: "legacy tile conversion contract coverage"
+ key_links:
+ - "ITileRawData.events → TileStore tile record → ITileStore.getEvent(num)"
+ - "legacy tile source → TileLegacyBridge → valid events map consumed by TileStore"
+---
+
+
+按用户最新 Tile contract clarification 独立迁移 Tile implementation,解除 Plan 01 Node tracer 与 Tile contract cleanup 的文件耦合。
+
+Purpose: 让四包 type gate 面对唯一用户认可的 `events`/`getEvent()` 模型,同时保持 Node fixture 与 Phase 5 legacy migration 边界清晰。
+Output: TileStore events-map implementation、legacy conversion、focused regression tests。
+Decision trace: D-01: closure coverage; D-02: stop on undefined contracts; D-03: explicit fixtures; D-04: fixed data tests; D-12: Node-safe legacy bridge; D-22: authoritative Tile events contract.
+
+
+
+@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
+@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/REQUIREMENTS.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/03-data-completion/03-CONTEXT.md
+@.planning/phases/03-data-completion/03-RESEARCH.md
+@packages-user/data-common/src/store/types.ts
+@packages-user/data-common/src/store/tileStore.ts
+@packages-user/data-state/src/legacy/tile.ts
+@dev.md
+
+
+
+
+
+ Task 1: Tile events raw-data 到 accessor tracer
+ packages-user/data-common/src/store/tileStore.ts, packages-user/data-common/src/store/tileStore.test.ts
+ packages-user/data-common/src/store/types.ts, packages-user/data-common/src/store/tileStore.ts, packages-user/data-common/src/store/eventStore.test.ts, packages-user/data-base/src/map/types.ts, .planning/phases/03-data-completion/03-CONTEXT.md, dev.md
+ 严格以当前 `ITileRawData.events` 与 `ITileStore.getEvent(num)` 为实现事实源:让 TileStore 保存每个 tile 的默认事件映射,`getEvent(num)` 返回对应的只读事件 map,缺失 tile 返回空 map,并保持现有 id/num 索引与重复 tile 替换语义。不要新增 trigger 标量兼容字段、不要修改用户-owned types.ts、不要改变 map/event executor 的公共语义。新增 focused tests 覆盖默认 map、缺失 tile、id/num 查询、重复 tile 替换和事件 map 不被外部写入;每个 `it` 前添加 dev.md 要求的中文覆盖注释 (D-01, D-02, D-04, D-22)。
+
+ $lines = Get-Content -LiteralPath "packages-user/data-common/src/store/tileStore.test.ts"; for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -match '^\s*it(?:\.[A-Za-z]+)*\s*\(') { if ($i -eq 0 -or $lines[$i - 1] -notmatch '^\s*//\s*[\u4e00-\u9fff]') { throw "Missing Chinese coverage comment immediately before it at line $($i + 1)" } } }; pnpm exec vitest run packages-user/data-common/src/store/tileStore.test.ts
+ 测试非零、输出 failed、getEvent 返回值不是 events map、缺失 tile 不是安全空 map、重复 tile/index 行为回归,或实现重新读取 trigger 标量
+
+
+ - TileStore 与工作区现有 `ITileRawData.events`/`ITileStore.getEvent()` 完全一致 (D-22)
+ - raw events map 到 accessor 的单路径和缺失/重复索引边界均有 focused assertions (D-01, D-04)
+
+ TileStore 已按 events-map contract 工作,focused accessor tests 通过。
+
+
+
+ Task 2: Legacy tile conversion events-map regression
+ packages-user/data-state/src/legacy/tile.ts, packages-user/data-state/test/tileLegacy.test.ts
+ packages-user/data-common/src/store/types.ts, packages-user/data-common/src/store/tileStore.ts, packages-user/data-state/src/legacy/tile.ts, packages-user/data-state/src/legacy/interface.ts, dev.md
+ 调整 `TileLegacyBridge` 使 legacy tile source 转换为合法 `ITileRawData.events` map,保留当前 legacy host 的必要数据转换但不扩大到 Phase 5 的删除/迁移工作。测试使用显式 legacy-like fixture,验证转换后的 events map 可由 TileStore/getEvent 消费、缺失事件输入安全地产生合法默认值,并确认转换路径没有把旧 trigger 标量作为输出契约;不依赖尚未由后续 Node replay 计划固定的共享闭环 fixture,也不引入 browser global stub 到 Node fixture。`tileLegacy.test.ts` 中每一个 `it` 调用前必须紧邻一行说明当前覆盖内容的中文单行注释,逐条遵守 `dev.md:85` (D-01, D-03, D-12, D-22)。
+
+ $files = @("packages-user/data-common/src/store/tileStore.test.ts", "packages-user/data-state/test/tileLegacy.test.ts"); foreach ($file in $files) { if (-not (Test-Path -LiteralPath $file)) { throw "Missing Tile test file $file" }; $lines = Get-Content -LiteralPath $file; for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -match '^\s*it(?:\.[A-Za-z]+)*\s*\(') { if ($i -eq 0 -or $lines[$i - 1] -notmatch '^\s*//\s*[\u4e00-\u9fff]') { throw "Missing Chinese coverage comment immediately before it in $file at line $($i + 1)" } } } }; pnpm exec vitest run packages-user/data-state/test/tileLegacy.test.ts packages-user/data-common/src/store/tileStore.test.ts
+ 任一 `it` 调用没有前置中文单行覆盖注释、任一测试非零、legacy conversion 输出不是合法 events map、Node 测试依赖 main/window/document 全局,或 Phase 5 legacy-only 文件被扩大修改
+
+
+ - Legacy conversion 与 TileStore 共享同一 events-map contract,不恢复 trigger scalar (D-22)
+ - `packages-user/data-common/src/store/tileStore.test.ts` 与 `packages-user/data-state/test/tileLegacy.test.ts` 的每个 `it` 前都有符合 `dev.md:85` 的中文单行覆盖注释,且 focused tests 只覆盖 DATA-01 需要的 Tile bridge 路径 (D-01, D-04)
+
+ Tile implementation 与必要 legacy conversion 已迁移到用户锁定 contract,Plan 01/04/05 可消费统一结果。
+
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|---|---|
+| legacy tile input → TileLegacyBridge | legacy-shaped values become data-side tile/event state |
+| raw events map → TileStore | fixture/event identifiers become indexed runtime lookup data |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
+|---|---|---|---|---|---|
+| T-03-13 | Tampering | legacy tile/event conversion | medium | mitigate | Consume the locked events-map contract, validate missing values through safe defaults, and cover conversion with explicit fixtures. |
+| T-03-14 | Tampering | TileStore event lookup | low | mitigate | Return a safe empty map for missing tiles and test index/replacement behavior without exposing mutable internal maps. |
+| T-03-SC | Tampering | npm/pip/cargo installs | low | accept | No package installation is planned. |
+
+
+
+- `pnpm exec vitest run packages-user/data-common/src/store/tileStore.test.ts packages-user/data-state/test/tileLegacy.test.ts`
+- The four-package type/circular gates in Plan 03-05 consume this implementation after all Phase 3 integration changes.
+
+
+
+The user-owned Tile events contract is implemented end-to-end from raw and legacy conversion through TileStore lookup, with focused Node-safe regression coverage and no trigger-scalar restoration.
+
+
+
diff --git a/.planning/phases/03-data-completion/03-RESEARCH.md b/.planning/phases/03-data-completion/03-RESEARCH.md
index 9da17c1..0687437 100644
--- a/.planning/phases/03-data-completion/03-RESEARCH.md
+++ b/.planning/phases/03-data-completion/03-RESEARCH.md
@@ -2,7 +2,7 @@
**Researched:** 2026-09-10
**Domain:** TypeScript data-layer integration, deterministic replay, and Node-only validation
-**Confidence:** MEDIUM — the implementation map and current failures are verified; several public-contract decisions are intentionally unresolved
+**Confidence:** MEDIUM — the implementation map and current failures are verified; the latest user decisions resolve the previously open public-contract questions, while implementation details remain subject to the locked checkpoints below
## User Constraints (from CONTEXT.md)
@@ -36,6 +36,20 @@
- **D-16:** `data-common`、`data-base`、`data-system`、`data-state` 四层的 TypeScript 错误全部清零,并针对这四层检查循环引用。渲染端或 legacy-only 的无关问题不扩大为本阶段范围。
- **D-17:** 本阶段系统级任务较多,遇到任何接口语义、系统边界、依赖关系或实现路径上的不确定问题,都必须暂停并提问确认,不得擅自选择“看起来合理”的方案绕过问题。
+### Latest Locked Clarifications
+
+- **D-18:** Node uses an in-memory save adapter, resets from fixed initial state, and compares the approved key snapshots; this policy is constrained by the later no-options factory clarification.
+- **D-19:** Legacy converter and data source access use a centralized internal injected dependency boundary; `CoreState` does not read legacy globals directly.
+- **D-20:** The circular gate covers the four data packages internally and at their mutual boundaries, while unrelated render/legacy-only code remains outside the scope.
+- **D-21:** Add `pnpm test:data-node`, using `script/test-data-node.ts` or an equivalent Node runner, with fixtures under `packages-user/data-state/test/fixtures/`.
+- **D-22:** `ITileRawData.events` and `ITileStore.getEvent(num)` are authoritative; the scalar `trigger` implementation migrates to this contract.
+- **D-23:** `createCoreState()` has no options, directly calls `new CoreState()`, and keeps main initialization in the constructor for now.
+- **D-24:** Event built-ins cover map set/dynamic-move-static with optional safe/delete, hero sequence/forward/front `onTouch`, and temporary event sequence/id insertion.
+- **D-25:** Replay commands are registered in stable order: up, right, down, left, auto-pathfind to point, use item, equip, unequip.
+- **D-26:** Final Node replay compares only end-of-playback full hero attributes and every map matrix; per-step hero checks are unit-test diagnostics only.
+- **D-27:** Built-ins use `(param, env)` with `IBlockEventEnv`; approved names are the eight event functions in CONTEXT.md, and failure safely skips with `void`.
+- **D-28:** `CoreState` is Node-safe without a browser/legacy host: skip legacy loading and IndexedDB, use memory-capable state, and keep the public factory parameterless.
+
### the agent's Discretion
没有授权 AI 在接口语义或系统边界上自行决策的事项。
@@ -68,9 +82,9 @@
The four data packages already expose a layered contract: `IDataCommon` owns stores, facing, direction mapping, and save; `IStateBase` adds maps, hero, enemies, flags, and saveable-content registration; `IStateSystem` adds enemy context and events; and `ICoreState` adds loading and save-executor wiring. [VERIFIED: packages-user/data-common/src/types.ts:46-63; packages-user/data-base/src/types.ts:17-41; packages-user/data-system/src/types.ts:6-11; packages-user/data-state/src/types.ts:15-31] The reusable replay primitives already provide binary route storage, command registration, reset-before-playback, sequential async execution, and hooks. [VERIFIED: packages-user/data-common/src/replay/types.ts:14-20,305-353; packages-user/data-common/src/replay/system.ts:50-80; packages-user/data-common/src/replay/sandbox.ts:118-145]
-The main Phase 3 risk is not missing isolated systems; it is the missing Node-safe composition boundary. `CoreState` currently constructs every layer directly, installs legacy converters, waits on global loading events, reads legacy globals, and wires a singleton through `ins.ts`. [VERIFIED: packages-user/data-state/src/core.ts:81-115,139-240; packages-user/data-state/src/ins.ts:1-10] A direct Node import currently fails before construction because the common logger evaluates `main` and DOM globals at module load. [VERIFIED: direct `pnpm exec tsx` import probe on 2026-09-10; packages/common/src/logger.ts:24-40] The plan must therefore start with a user-confirmation checkpoint for the factory, legacy dependency, replay command, snapshot, built-in-function, and save-backend contracts rather than inventing public behavior. [ASSUMED]
+The main Phase 3 risk is not missing isolated systems; it is the missing Node-safe composition boundary. `CoreState` currently constructs every layer directly, installs legacy converters, waits on global loading events, reads legacy globals, and wires a singleton through `ins.ts`. [VERIFIED: packages-user/data-state/src/core.ts:81-115,139-240; packages-user/data-state/src/ins.ts:1-10] A direct Node import currently fails before construction because the common logger evaluates `main` and DOM globals at module load. [VERIFIED: direct `pnpm exec tsx` import probe on 2026-09-10; packages/common/src/logger.ts:24-40] The latest decisions now lock the implementation boundary: `createCoreState()` is no-argument and directly calls `new CoreState()`, the constructor is Node-safe, legacy conversion is reached through an internal injected dependency boundary rather than direct globals, Node uses memory-capable state, replay codes are top-level stable enum values, event built-ins use the eight approved names, and first divergence throws a verifier-local error with index/code/params/reason. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:43-52; latest user clarification]
-**Primary recommendation:** keep public `types.ts` files unchanged until the user resolves the contract mismatches; then build a factory-first, dependency-injected Node composition around existing replay/save/event primitives, with a fixed event-bearing fixture and separate four-package gates. [ASSUMED]
+**Primary recommendation:** keep public `types.ts` files unchanged unless the locked current contract requires an implementation correction; build a factory-first, internally dependency-injected Node composition around existing replay/save/event primitives, with a fixed event-bearing fixture and separate four-package gates. Resolve any newly exposed interface mismatch through D-02/D-17 rather than guessing. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:18-22,40-52]
## Architectural Responsibility Map
@@ -140,23 +154,23 @@ The diagram follows the locked responsibility split: the runtime entry creates t
### Recommended Project Structure
-The exact new filenames are not locked and must be confirmed before implementation. [ASSUMED]
+The phase filenames used by the plan set are now fixed by the user decisions and existing workspace layout. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:43-52]
```text
packages-user/data-state/src/ # factory and final L0–L3 assembly
packages-user/data-system/src/ # event/combat/path registrations
packages-user/data-common/src/ # replay/save primitives and contracts
-script/ # dedicated Node verifier entry (name TBD)
+script/test-data-node.ts # dedicated Node verifier entry
packages-user/*/src/**/*.test.ts # closure tests near the owning package
```
### Pattern 1: Factory-first composition with compatibility singleton
-**What:** Add an explicit creation path that assembles a fresh four-layer instance, while retaining `state` only as a compatibility entry if the user confirms it remains needed. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:30-35; packages-user/data-state/src/ins.ts:1-10]
+**What:** Add the no-argument `createCoreState()` path that directly calls `new CoreState()`, while retaining `state` only as a compatibility entry where the existing browser host needs it. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:30-35,47-52; packages-user/data-state/src/ins.ts:1-10]
**When to use:** Use the factory for Node replay and render-side creation; do not make the singleton the only construction route. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:30-35]
-**Implementation guidance:** Put initialization order and final registration at the top level, and let subsystems expose registration helpers instead of copying their internals into `CoreState`. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:30-35; dev.md:46-53]
+**Implementation guidance:** Keep the main initialization order in the `CoreState` constructor for this phase, use an internal injected legacy dependency boundary so `CoreState` never reads legacy globals directly, and let subsystems expose registration helpers instead of copying their internals into `CoreState`. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:30-35,43-52; dev.md:46-53]
### Pattern 2: Fake/state fixtures with explicit dependencies
@@ -172,7 +186,7 @@ packages-user/*/src/**/*.test.ts # closure tests near the owning package
**Existing behavior:** `ReplaySandbox.step()` reads one route step, looks up its command, awaits `command.execute(next)`, then awaits `onStep` hooks. [VERIFIED: packages-user/data-common/src/replay/sandbox.ts:118-145]
-**Pitfall to resolve:** the existing public result is only `Promise`, while D-07 requires a first-divergence reason and index. Do not add an error shape or choose a logging protocol without user confirmation. [VERIFIED: packages-user/data-common/src/replay/types.ts:14-20; .planning/phases/03-data-completion/03-CONTEXT.md:24-29]
+**Locked failure policy:** retain the existing replay boolean command interface; the Node verifier stops at the first false/unknown/throw/mismatch by throwing an implementation-local error and reporting index, top-level stable command code, params, and reason. Do not add a new user-facing error class or alter the replay command interface. [VERIFIED: packages-user/data-common/src/replay/types.ts:14-20; .planning/phases/03-data-completion/03-CONTEXT.md:24-29,49-52; latest user clarification]
### Anti-Patterns to Avoid
@@ -207,7 +221,7 @@ The opened interface source says `ITileRawData` contains `events: Record
+pnpm test:data-node
```
- The first command is the current deterministic non-watch data-package test gate and passed 58 tests in this session. [VERIFIED: command run on 2026-09-10; package.json:8-10]
- `pnpm check:type` is necessary but currently includes unrelated packages; the phase gate must assert that no approved `data-common`, `data-base`, `data-system`, or `data-state` diagnostics remain rather than claiming the whole repository is clean. [VERIFIED: package.json:23-24; command output on 2026-09-10]
-- `pnpm check:circular` currently starts at `src/main.ts`; the explicit four-package madge invocation is needed to make D-16 observable. Its exact scope must be confirmed because current traversal includes `@motajs/common` cycles. [VERIFIED: package.json:23-24; targeted command output on 2026-09-10]
-- The final command must not be invented in the plan until its script name, fixture location, snapshot shape, and exit/report contract are approved. [ASSUMED]
+- `pnpm check:circular` currently starts at `src/main.ts`; the explicit four-package madge invocation is needed to make D-16 observable. Its scope is locked to cycles containing the four data-package nodes, while unrelated common-only dependencies remain outside the phase gate. [VERIFIED: package.json:23-24; targeted command output on 2026-09-10; .planning/phases/03-data-completion/03-CONTEXT.md:39-45]
+- `pnpm test:data-node` is fixed by D-21; its fixture path and end-only snapshot/report contract are fixed by D-13 through D-15 and D-26/D-28. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:36-52]
## Code Examples
@@ -304,7 +318,7 @@ Use this existing awaited boundary when a replay command represents a movement/e
**What goes wrong:** The implementation and `types.ts` disagree on tile events/triggers, so a superficial type edit could change the public data model. [VERIFIED: packages-user/data-common/src/store/types.ts:47-86; packages-user/data-common/src/store/tileStore.ts:23-29; packages-user/data-state/src/legacy/tile.ts:96-105]
-**How to avoid:** Stop at the contract checkpoint and ask which model is authoritative; only then change the implementation. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:18-22,40-44]
+**How to avoid:** Use the locked `events`/`getEvent()` model and migrate implementation-side consumers; if another interface mismatch appears, stop under D-02/D-17 instead of changing the user-owned contract. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:18-22,45-52]
### Pitfall 2: Making Node tests pass with a permanent browser-global stub
@@ -336,7 +350,7 @@ This is an integration/refactor phase, so the runtime inventory is required. [VE
| Category | Items Found | Action Required |
|----------|-------------|-----------------|
-| Stored data | Existing saveable content is registered under `@system/hero`, `@system/flags`, `@system/maps`, and `@system/enemy`; `SaveSystem` persists a `Map`. [VERIFIED: packages-user/data-state/src/core.ts:213-224; packages-user/data-common/src/save/system.ts:132-174] | Confirm whether the Node fixture uses in-memory save maps only or must initialize Dexie; this is a contract checkpoint, not a guessed migration. [ASSUMED] |
+| Stored data | Existing saveable content is registered under `@system/hero`, `@system/flags`, `@system/maps`, and `@system/enemy`; `SaveSystem` persists a `Map`. [VERIFIED: packages-user/data-state/src/core.ts:213-224; packages-user/data-common/src/save/system.ts:132-174] | Use the locked in-memory Node path and fixture reset; do not initialize Dexie/IndexedDB in Node. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:36-52] |
| Live service config | None found in the inspected repository sources; the current phase inputs mention no external service configuration. [ASSUMED] | None unless the user identifies a runtime service. |
| OS-registered state | None found in the inspected repository sources; no task/service registration is part of the current scripts or phase context. [ASSUMED] | None. |
| Secrets/env vars | None found in the inspected Phase 3 data sources; current construction instead reads legacy globals such as `core`. [VERIFIED: packages-user/data-state/src/core.ts:220-234] | Replace global reads with the approved injected dependency boundary; do not introduce environment-variable names without a decision. [ASSUMED] |
@@ -356,7 +370,7 @@ This is an integration/refactor phase, so the runtime inventory is required. [VE
| `indexedDB` | Dexie-backed browser save initialization | ✓ absent | `undefined` | Confirm an injected/in-memory Node save policy before factory implementation. [VERIFIED: Node probe on 2026-09-10; packages-user/data-common/src/save/system.ts:61-67] |
**Missing dependencies with no fallback:** None for the existing commands. [VERIFIED: tool probes on 2026-09-10]
-**Missing dependencies with fallback:** IndexedDB is absent; the fallback policy is not locked and must be confirmed before implementing Node save initialization. [ASSUMED]
+**Missing dependencies with fallback:** IndexedDB is absent; the locked fallback is memory-capable state with fixture/explicit loading, not a browser database. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:45-52]
## Validation Architecture
@@ -373,8 +387,9 @@ This is an integration/refactor phase, so the runtime inventory is required. [VE
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|--------|----------|-----------|-------------------|-------------|
-| DATA-01 | Fresh top-level instance can execute a fixed replay containing a player action and event/state mutation, then match exact snapshots and end normally | integration / Node smoke | `pnpm exec ` | ❌ Wave 0/1 contract and fixture gap [ASSUMED] |
+| DATA-01 | Fresh top-level instance can execute a fixed replay containing a player action and event/state mutation, then match exact snapshots and end normally | integration / Node smoke | `pnpm test:data-node` | ❌ Wave 1 fixture and registry gap [ASSUMED] |
| DATA-01 | Existing data-side closure tests remain green | unit/integration | `pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state` | ✅ existing tests; replay closure is incomplete [VERIFIED: command run and inspected test files on 2026-09-10] |
+| DATA-01 | Enemy creation/attribute or save behavior, Flag set/read/save-load, deterministic combat damage, saveable round trip, trigger/event mutation, and replay ordering/async-stop behavior | focused closure | `pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state` | ❌ `dataClosure.test.ts` is a planned Phase 3 artifact [ASSUMED] |
| DATA-01 | Four data packages have no TypeScript diagnostics | type gate | `pnpm check:type` plus the approved four-package diagnostic assertion | ❌ dedicated assertion is not present in package scripts [VERIFIED: package.json:6-24] |
| DATA-01 | Four data packages have no approved circular dependencies | static analysis | `pnpm exec madge --circular --extensions ts packages-user/data-common/src packages-user/data-base/src packages-user/data-system/src packages-user/data-state/src` | ❌ current command reports 15 cycles [VERIFIED: targeted command run on 2026-09-10] |
@@ -384,13 +399,12 @@ This is an integration/refactor phase, so the runtime inventory is required. [VE
- **Per wave merge:** the same data-package suite plus the focused type and circular commands above. [ASSUMED]
- **Phase gate:** dedicated Node replay verifier, exact snapshots, data tests, approved four-package type gate, and approved circular gate all green before verification. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:36-41]
-### Wave 0 Gaps
+### Locked pre-execution gates
-- [ ] User-approved factory and dependency bundle; no public factory contract currently exists. [VERIFIED: packages-user/data-state/src/types.ts:15-31]
-- [ ] User-approved stable replay command codes, error-reporting contract, and fixed fixture format. [VERIFIED: packages-user/data-common/src/replay/types.ts:14-20,316-353]
-- [ ] User-approved exact snapshot fields and reset/save backend policy. [VERIFIED: packages-user/data-common/src/replay/types.ts:105-110; packages-user/data-state/src/types.ts:15-31]
-- [ ] Minimal built-in-function names/signatures and state effects. [VERIFIED: packages-user/data-system/src/event/types.ts:34-46; packages-user/data-system/src/event/system.ts:11-18]
-- [ ] Node-safe logger/loading import boundary; current direct import fails on missing `main`. [VERIFIED: packages/common/src/logger.ts:24-40; direct import probe on 2026-09-10]
+- [x] No-argument factory, constructor initialization, internal legacy dependency boundary, and Node-safe memory path are locked. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:43-52]
+- [x] Stable top-level replay enum order and first-divergence thrown diagnostic fields are locked. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:48-52; latest user clarification]
+- [x] Eight event built-in names, `(param, env)`, `IBlockEventEnv`, scope, and safe `void` failure are locked. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:47-52]
+- [x] End-only full hero/map snapshot comparison, Node command, Tile events contract, and four-package circular boundary are locked. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:36-52]
## Security Domain
@@ -415,44 +429,28 @@ Security enforcement is enabled at ASVS level 1 in project configuration. [VERIF
| Unknown or false replay command | Tampering / Denial of service | Stop immediately, report the approved index/code/params/reason, and return non-zero from the Node verifier. [VERIFIED: packages-user/data-common/src/replay/sandbox.ts:124-141; .planning/phases/03-data-completion/03-CONTEXT.md:24-29,36-41] |
| Browser globals evaluated in Node | Information/control-flow boundary failure | Keep `window`/DOM/render modules out of the Node import graph and use explicit dependencies. [VERIFIED: packages/common/src/logger.ts:24-40; packages-user/entry-data/src/mota.ts:116-154; .planning/phases/03-data-completion/03-CONTEXT.md:30-35] |
-## Open Questions / Blockers Requiring User Confirmation
+## Resolved Contract Status
-1. **What is the exact factory contract?** `ICoreState` has no creation method/options, and current `CoreState` has a zero-argument constructor with global legacy reads. [VERIFIED: packages-user/data-state/src/types.ts:15-31; packages-user/data-state/src/core.ts:115-234]
- - **Blocker:** adding a public factory/options object or changing `ICoreState` is an interface decision.
- - **Ask:** approve the factory name/signature, dependency bundle, returned replay access, and singleton compatibility policy.
+The seven questions previously listed as blockers are resolved by the latest CONTEXT decisions and user clarifications. They are recorded here so execution does not reopen them:
-2. **Which tile contract is authoritative?** `ITileRawData.events`/`getTrigger(): number[]` conflict with `TileStore.trigger`/`TileLegacyBridge.trigger`. [VERIFIED: packages-user/data-common/src/store/types.ts:47-86; packages-user/data-common/src/store/tileStore.ts:23-29; packages-user/data-state/src/legacy/tile.ts:96-105]
- - **Blocker:** either direction changes public semantics.
- - **Ask:** confirm the intended field and trigger cardinality before type cleanup.
+1. **Factory and construction:** `createCoreState()` has no options and directly calls `new CoreState()`; main initialization remains in the constructor. The constructor must select a Node-safe memory path when no legacy/browser host exists, while the compatibility singleton remains available for the browser path. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:43-52]
+2. **Tile contract:** `ITileRawData.events` and `ITileStore.getEvent(num)` are authoritative; the implementation and legacy conversion migrate away from the scalar `trigger` shape. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:45-47]
+3. **Replay ownership:** replay commands are registered at the top level using stable enum values in the locked order: up, right, down, left, auto-pathfind to point, use item, equip, unequip. The initial tracer uses a private direct `ReplaySystem` harness; the final registry is assembled by `CoreState` after the replay contract checkpoint. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:48-50; revised 03-01/03-03 plans]
+4. **First-divergence diagnostics:** the Node verifier fails immediately by throwing an implementation-local error and reports index, top-level command code, params, and reason. The public replay boolean interface and a new user-facing error class are not changed. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:49-52; latest user clarification]
+5. **Event built-ins:** the approved names are `eventSetBlock`, `eventMoveBlock`, `eventDeleteBlock`, `eventMoveHero`, `eventMoveHeroStep`, `eventTouchFront`, `eventInsertEvents`, and `eventInsertEvent`; all use `(param, env)` with `IBlockEventEnv` and safe `void` failure. Scope is map set/dynamic-move-static with optional safe/delete, hero sequence/forward/front `onTouch`, and temporary sequence/id insertion. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:47-52]
+6. **Node save and snapshot:** Node uses memory-capable state and the fixed fixture/reset path; final replay comparison is end-only for full hero attributes and every map matrix. Per-step hero checks remain unit-test diagnostics only. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:36-52]
+7. **Circular boundary:** the gate covers the four data packages internally and at their mutual boundaries; unrelated render/legacy-only code is not widened into the gate. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:39-45]
-3. **Where does replay live, and what are stable command codes?** No L0–L3 contract exposes `ReplaySystem`, while D-08 requires top-level stable registration. [VERIFIED: packages-user/data-common/src/types.ts:46-68; packages-user/data-base/src/types.ts:17-41; packages-user/data-state/src/types.ts:15-31; .planning/phases/03-data-completion/03-CONTEXT.md:24-35]
- - **Blocker:** choosing a property, factory return shape, or numeric allocation would invent interface behavior.
- - **Ask:** provide the ownership and code allocation policy.
-
-4. **How should first-divergence diagnostics be represented?** Public replay commands return only `Promise`, while D-07 requires index, code, params, and reason. [VERIFIED: packages-user/data-common/src/replay/types.ts:14-20; .planning/phases/03-data-completion/03-CONTEXT.md:24-29]
- - **Blocker:** logger-only, thrown-error, hook, and result-object approaches have different public behavior.
- - **Ask:** choose the approved mechanism without changing the interface unilaterally.
-
-5. **What is the minimum built-in set and each signature?** `IGameEventInit.addBuiltinFunction` exists, but `GameEventSystem` constructs the interpreter with empty built-in/global arrays and does not expose that initializer. [VERIFIED: packages-user/data-system/src/event/types.ts:34-46; packages-user/data-system/src/event/system.ts:7-22]
- - **Blocker:** event function names and mutations are user-owned semantics.
- - **Ask:** list the minimal Node-fixture built-ins and their exact behavior.
-
-6. **What is the Node save policy and snapshot shape?** `SaveSystem.init` creates a Dexie database, but current Node has no `indexedDB`; `ICoreState` has no aggregate snapshot API. [VERIFIED: packages-user/data-common/src/save/system.ts:61-67; Node probe on 2026-09-10; packages-user/data-state/src/types.ts:15-31]
- - **Blocker:** in-memory save, injected adapter, no DB initialization, and fixture-only save maps are materially different choices.
- - **Ask:** identify the exact save backend, reset path, compression, and fields that must be compared.
-
-7. **What is the circular-check boundary?** Current targeted madge reports cycles through `@motajs/common` as well as data-package cycles, while D-16 names only four data layers. [VERIFIED: targeted madge output on 2026-09-10; .planning/phases/03-data-completion/03-CONTEXT.md:36-41]
- - **Blocker:** changing common logger/types may expand scope beyond Phase 3.
- - **Ask:** confirm whether all transitive cycles must disappear or only cycles whose nodes are in the four data packages.
+The remaining checkpoints in the plans are implementation-record gates for these locked contracts, not unresolved design questions. If execution exposes a genuinely new interface ambiguity, D-02/D-17 still require pausing for user clarification.
## Assumptions Log
| # | Claim | Section | Risk if wrong |
|---|-------|---------|--------------|
-| A1 | A new explicit factory and/or verifier entry can be added without changing the user-owned Layer 3 interface. | Summary / Work Breakdown | The plan may target an API the user rejects. |
-| A2 | The fixed replay fixture can use existing saveable objects for exact snapshots once the user selects fields and compression. | Runtime State / Validation | Snapshot comparison may be impossible without a new approved contract. |
-| A3 | The Node-safe boundary may require changes outside the four data packages, such as the common logger/import graph. | Work Breakdown / Security | The phase scope or dependency graph may need a user-approved adjustment. |
-| A4 | The eventual dedicated command name and fixture file path are not yet user-owned decisions. | Verification Commands | A plan with a guessed command would be non-reproducible. |
+| A1 | The no-argument factory and verifier entry can be added without changing the user-owned Layer 3 interface. | Summary / Work Breakdown | LOW — constrained by D-23/D-28 and the implementation-local Node boundary. |
+| A2 | The fixed replay fixture can use existing saveable objects for exact end snapshots. | Runtime State / Validation | LOW — D-26 fixes the compared hero/map outputs; use D-02 if an existing saveable lacks a defined representation. |
+| A3 | The Node-safe boundary may require changes to the common logger/import graph while keeping the circular gate scoped to the four data packages. | Work Breakdown / Security | LOW — the change is permitted only where required for Node import safety; unrelated render/legacy-only diagnostics remain outside the gate. |
+| A4 | The dedicated command and fixture paths are fixed by D-21. | Verification Commands | LOW — `pnpm test:data-node`, `script/test-data-node.ts`, and `packages-user/data-state/test/fixtures/`. |
## State of the Art
@@ -478,13 +476,13 @@ Security enforcement is enabled at ASVS level 1 in project configuration. [VERIF
### Tertiary (LOW confidence)
-- None used; unresolved design choices are marked `[ASSUMED]` and listed in the Assumptions Log. [VERIFIED: this artifact]
+- None used; implementation assumptions are constrained by the resolved contract status and must not reopen locked decisions. [VERIFIED: this artifact]
## Metadata
**Confidence breakdown:**
- Standard stack: HIGH — versions and commands were read from repository manifests/config and probed locally. [VERIFIED: package.json:6-24,81-98; vite.config.ts:46-49]
-- Architecture: HIGH for current code, MEDIUM for the target because factory/replay/snapshot contracts are missing. [VERIFIED: inspected source files and Open Questions above]
+- Architecture: HIGH for current code, MEDIUM for the target because implementation seams still need to be wired, but the factory/replay/snapshot contracts are now locked. [VERIFIED: inspected source files and Resolved Contract Status above]
- Pitfalls: HIGH for observed global/type/circular failures, MEDIUM for remediation scope because D-17 forbids guessing. [VERIFIED: command results and source citations above]
**Research date:** 2026-09-10
diff --git a/.planning/phases/03-data-completion/03-VALIDATION.md b/.planning/phases/03-data-completion/03-VALIDATION.md
new file mode 100644
index 0000000..cbbcf98
--- /dev/null
+++ b/.planning/phases/03-data-completion/03-VALIDATION.md
@@ -0,0 +1,97 @@
+---
+phase: "03"
+slug: "data-completion"
+status: draft
+nyquist_compliant: false
+wave_0_complete: false
+created: "2026-09-10"
+---
+
+# Phase 03 — Validation Strategy
+
+> Per-phase validation contract for feedback sampling during execution.
+
+---
+
+## Test Infrastructure
+
+| Property | Value |
+|----------|-------|
+| **Framework** | Vitest 4.0.18 |
+| **Config file** | `vite.config.ts` |
+| **Quick run command** | `pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state` |
+| **Full suite command** | `& pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }; & pnpm test:data-node; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }; & pnpm exec tsx script/check-data-type.ts; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }; & pnpm exec tsx script/check-data-circular.ts; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }` |
+| **Estimated runtime** | ~60 seconds |
+
+---
+
+## Sampling Rate
+
+- **After every task commit:** Run `pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state`
+- **After every plan wave:** Run the full suite command above
+- **Before `/gsd-verify-work`:** Full suite must be green
+- **Max feedback latency:** 60 seconds
+
+---
+
+## Per-Task Verification Map
+
+| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
+|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
+| 03-01-01 | 01 | 1 | DATA-01 | T-03-02, T-03-03 | Exact singleton/barrel back-edges are absent before fresh CoreState construction | dependency/integration | `pnpm exec madge --json --extensions ts packages-user/data-state/src/enemy/calculator.ts packages-user/data-state/src/legacy/move.ts` plus direct `createCoreState()` probe | ✅ source | ⬜ pending |
+| 03-01-02 | 01 | 1 | DATA-01 | T-03-01, T-03-03 | Fixed Node tracer awaits movement and event mutation without browser globals | integration | `pnpm exec vitest run packages-user/data-state/test/nodeTracer.test.ts` | ❌ planned | ⬜ pending |
+| 03-02-01 | 02 | 2 | DATA-01 | T-03-04 | All eight approved built-in contracts are recorded before implementation | contract checkpoint | `Get-Content -Raw .planning/phases/03-data-completion/03-EVENT-CONTRACT.md` field/name assertion | ❌ planned | ⬜ pending |
+| 03-02-02 | 02 | 2 | DATA-01 | T-03-04, T-03-05 | Approved event built-ins mutate only intended data state and safely await actions | unit/integration | `pnpm exec vitest run packages-user/data-state/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts` | ❌ planned | ⬜ pending |
+| 03-03-01 | 03 | 3 | DATA-01 | T-03-06 | Replay enum ownership, order, and access boundary are recorded | contract checkpoint | `Get-Content -Raw .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md` order/field assertion | ❌ planned | ⬜ pending |
+| 03-03-02 | 03 | 3 | DATA-01 | T-03-06, T-03-07 | Async replay decorators and module command items preserve completion and failure | unit | `pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts` | ❌ planned | ⬜ pending |
+| 03-03-03 | 03 | 3 | DATA-01 | T-03-06, T-03-07 | CoreState owns one ordered eight-command registry and the compatibility barrel remains reachable | unit/integration | `pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts` and the focused four-package suite | ❌ planned | ⬜ pending |
+| 03-04-01 | 04 | 4 | DATA-01 | T-03-09 | First-divergence thrown diagnostic fields and stop policy are recorded | contract checkpoint | `Get-Content -Raw .planning/phases/03-data-completion/03-REPLAY-DIAGNOSTICS.md` field assertion | ❌ planned | ⬜ pending |
+| 03-04-02 | 04 | 4 | DATA-01 | T-03-08, T-03-09, T-03-10 | Dedicated Node replay stops at the first divergence and compares end snapshots | integration/process | `pnpm test:data-node` | ❌ planned | ⬜ pending |
+| 03-04-03 | 04 | 4 | DATA-01 | T-03-09, T-03-10 | Success and unknown/false/throw/snapshot mismatch branches are deterministic | unit/integration | `pnpm exec vitest run packages-user/data-state/test/nodeReplay.test.ts packages-user/data-state/test/coreNode.test.ts` | ❌ planned | ⬜ pending |
+| 03-05-01 | 05 | 5 | DATA-01 | T-03-15 | D-20 common/data-common cycle closure contract is approved | contract checkpoint | `Get-Content -Raw .planning/phases/03-data-completion/03-COMMON-CYCLE-CONTRACT.md` boundary assertion | ❌ planned | ⬜ pending |
+| 03-05-02 | 05 | 5 | DATA-01 | T-03-11, T-03-15 | Scoped type and transitive circular gates fail closed on in-scope diagnostics | static analysis | `pnpm exec tsx script/check-data-circular.ts` then `pnpm exec tsx script/check-data-type.ts` | ❌ planned | ⬜ pending |
+| 03-05-03 | 05 | 5 | DATA-01 | T-03-11, T-03-12 | Focused DATA-01 closure and final data-side gates are deterministic | integration/quality gate | `pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state` plus Node/type/circular/lint/Prettier gates | ❌ planned | ⬜ pending |
+| 03-06-01 | 06 | 2 | DATA-01 | T-03-14 | TileStore exposes the approved events map and safe lookup behavior | unit | `pnpm exec vitest run packages-user/data-common/src/store/tileStore.test.ts` | ❌ planned | ⬜ pending |
+| 03-06-02 | 06 | 2 | DATA-01 | T-03-13 | Legacy conversion preserves the events map and every `it` has the required Chinese coverage comment | unit/static | comment assertion plus `pnpm exec vitest run packages-user/data-state/test/tileLegacy.test.ts packages-user/data-common/src/store/tileStore.test.ts` | ❌ planned | ⬜ pending |
+
+*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
+
+---
+
+## Wave 0 Requirements
+
+- [ ] `.planning/phases/03-data-completion/03-EVENT-CONTRACT.md` — approved eight built-in contract record
+- [ ] `.planning/phases/03-data-completion/03-REPLAY-CONTRACT.md` — approved stable replay command record
+- [ ] `.planning/phases/03-data-completion/03-REPLAY-DIAGNOSTICS.md` — approved first-divergence diagnostic record
+- [ ] `.planning/phases/03-data-completion/03-COMMON-CYCLE-CONTRACT.md` — approved D-20 cycle-closure record
+- [ ] `packages-user/data-state/test/nodeTracer.test.ts` — Node construction and replay tracer tests
+- [ ] `packages-user/data-state/test/fixtures/closed-loop.ts` — deterministic replay fixture
+- [ ] `packages-user/data-state/src/event/event.test.ts` — approved built-in behavior tests
+- [ ] `packages-user/data-state/src/replay/commands.test.ts` — ordered and awaited command tests
+- [ ] `packages-user/data-state/test/nodeReplay.test.ts` — first-divergence regression tests
+- [ ] `packages-user/data-state/test/coreNode.test.ts` — independent factory regression tests
+- [ ] `packages-user/data-state/test/dataClosure.test.ts` — focused DATA-01 closure tests
+- [ ] `packages-user/data-common/src/store/tileStore.test.ts` — Tile events-map tests
+- [ ] `packages-user/data-state/test/tileLegacy.test.ts` — legacy Tile conversion tests
+- [ ] `script/test-data-node.ts` — dedicated Node verifier
+- [ ] `script/check-data-type.ts` — scoped four-package type gate
+- [ ] `script/check-data-circular.ts` — scoped circular-dependency gate
+
+---
+
+## Manual-Only Verifications
+
+All Phase 3 behaviors have automated verification. User decision checkpoints in the plans are planning gates, not manual acceptance tests.
+
+---
+
+## Validation Sign-Off
+
+- [ ] All tasks have `` verify or Wave 0 dependencies
+- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
+- [ ] Wave 0 covers all missing references
+- [ ] No watch-mode flags
+- [ ] Feedback latency < 60s
+- [ ] `nyquist_compliant: true` set in frontmatter
+
+**Approval:** pending