docs(03): finalize data completion plans

This commit is contained in:
unanmed 2026-09-10 15:06:26 +08:00
parent 9da70d95de
commit e1787f3b0c
9 changed files with 1109 additions and 61 deletions

View File

@ -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: 渲染适配与双布局

View File

@ -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"
---
<objective>
先交付一个生产质量的 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.
</objective>
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/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
</context>
<tasks>
<task type="tracer">
<name>Task 1: singleton back-edge 修复与 Node-safe CoreState construction tracer</name>
<files>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</files>
<read_first>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</read_first>
<action>先完成本任务的两个反向依赖修复,再让构造 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 boundaryCoreState 只能消费该 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)。</action>
<verify>
<automated>$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');"</automated>
<fails_when>依赖图仍包含精确的 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 非零</fails_when>
</verify>
<acceptance_criteria>
- `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)
</acceptance_criteria>
<done>CoreState 构造路径所依赖的 enemy 与 legacy singleton back-edge 已在 Wave 1 明确闭合。</done>
</task>
<task type="auto">
<name>Task 2: Node-safe CoreState 与最小 replay→move→event→map tracer</name>
<files>packages-user/data-state/test/fixtures/closed-loop.ts, packages-user/data-state/test/nodeTracer.test.ts</files>
<read_first>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</read_first>
<action>消费 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)。</action>
<verify>
<automated>pnpm exec vitest run packages-user/data-state/test/nodeTracer.test.ts</automated>
<fails_when>测试非零退出、Node tracer 触碰 DOM/IndexedDB、没有创建新 CoreState、没有等待移动 Promise、事件没有改变固定地图矩阵、录像未正常结束或任一 `it` 缺少中文覆盖注释</fails_when>
</verify>
<acceptance_criteria>
- `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)
</acceptance_criteria>
<done>Wave 1 已证明修复 back-edge 后的 Node-safe constructor/factory 与最小 replay tracer 可重复通过;独立 Node runner 由后续计划接管。</done>
</task>
</tasks>
<threat_model>
## 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. |
</threat_model>
<verification>
- `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.
</verification>
<success_criteria>
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.
</success_criteria>
<output>
Create `.planning/phases/03-data-completion/03-01-SUMMARY.md` when done
</output>

View File

@ -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"
---
<objective>
在已有 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.
</objective>
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/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
</context>
<tasks>
<task type="checkpoint:decision" gate="blocking-human">
<name>Task 1: 固化八个 event built-in 的参数字段与注册语义</name>
<files>.planning/phases/03-data-completion/03-EVENT-CONTRACT.md</files>
<read_first>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</read_first>
<decision>记录并批准八个 event built-in 的确切参数接口、失败边界、临时事件插入语义与 AnonTokyo 注册 seam记录必须符合 D-24/D-27 的锁定范围</decision>
<context>用户已锁定八个名称、`(param, env)`、`IBlockEventEnv`、安全 void 失败以及地图/玩家/事件三组范围;当前源码仍只定义了部分参数,因此实现前必须把剩余字段与注册 seam 写成可执行记录,而不是由实现者补猜。</context>
<options>
<option id="confirm-record"><name>按锁定范围逐项记录并确认</name><pros>保留用户已锁定的名称、签名和行为,后续实现可直接执行</pros><cons>仍需把现有源码未表达的字段写入契约记录</cons></option>
<option id="edit-record"><name>直接编辑契约文件</name><pros>用户可直接补充最终字段、失败和注册 seam</pros><cons>必须保证八个函数全部有明确结论且不超出锁定范围</cons></option>
</options>
<action>暂停并在 `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)。</action>
<verify>
<automated>$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 } }</automated>
<fails_when>文件缺失、任一批准名称未出现、任一函数缺少字段/失败/注册结论、记录含 TBD/待确认/未确认/用户确认中,或没有记录 `param`、`env`、`IBlockEventEnv`、safe、onTouch 与 void 语义</fails_when>
</verify>
<acceptance_criteria>
- 八个候选名称、每个参数接口字段、失败行为、异步等待边界和注册方式均有用户确认记录 (D-06, D-17, D-27)
- 后续实现任务只消费该记录,不自行新增事件函数或字段 (D-11, D-24)
</acceptance_criteria>
<resume-signal>回复 confirm-record、edit-record或直接修改契约文件后回复 approved记录必须保持 D-24/D-27 的锁定范围</resume-signal>
<done>事件公共契约被用户逐项确认并记录Task 2 可无歧义实现。</done>
</task>
<task type="auto">
<name>Task 2: 实现最小 event built-ins、默认注册项与行为测试</name>
<files>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</files>
<read_first>.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</read_first>
<action>逐字消费 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→staticsafe 分支、delete、hero sequence/forward/front touch、temporary event sequence/id每个 `it` 前写中文覆盖注释 (D-01, D-03, D-04, D-10, D-11)。</action>
<verify>
<automated>pnpm exec vitest run packages-user/data-state/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts</automated>
<fails_when>任一测试非零退出、输出 failed、任何函数未安全跳过失败输入、异步动作未完成就返回、未批准函数被注册或 built-in 名称/参数与 checkpoint 记录不一致</fails_when>
</verify>
<acceptance_criteria>
- 八个函数全部从 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)
</acceptance_criteria>
<done>最小事件内建函数闭环完成并通过 focused data-side tests未扩大到完整 legacy 事件目录。</done>
</task>
</tasks>
<threat_model>
## 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. |
</threat_model>
<verification>
- 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.
</verification>
<success_criteria>
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.
</success_criteria>
<output>
Create `.planning/phases/03-data-completion/03-02-SUMMARY.md` when done
</output>

View File

@ -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"
---
<objective>
扩展 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.
</objective>
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/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
</context>
<tasks>
<task type="checkpoint:decision" gate="blocking-human">
<name>Task 1: 固化 replay 数值码与 top-level replay 访问边界</name>
<files>.planning/phases/03-data-completion/03-REPLAY-CONTRACT.md</files>
<read_first>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</read_first>
<decision>记录并批准八个 replay command 的稳定 top-level enum code、D-25 注册顺序与 CoreState/Node runner 访问入口</decision>
<context>用户已锁定命令顺序、top-level stable enum ownership 与失败策略;实现前仍须逐项记录 enum code 名称/值、访问边界及 pathfinding 等待点,不能遗漏或自行增加公共访问面。</context>
<options>
<option id="confirm-record"><name>按锁定顺序记录稳定 enum code 与访问入口</name><pros>保留 top-level 统一注册和既有用户命令顺序</pros><cons>需要把八项 code、访问 seam 与等待边界逐项写全</cons></option>
<option id="edit-record"><name>直接编辑契约文件</name><pros>用户可提供既有录像兼容所需的准确 enum code 与 runner 获取方式</pros><cons>必须同时给出八项 code、顺序与访问语义</cons></option>
</options>
<action>暂停并在 `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)。</action>
<verify>
<automated>$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 }</automated>
<fails_when>文件缺失、八个命令名称/顺序任一项没有逐项记录、稳定 enum code、CoreState/Node runner access 或 top-level registry 任一项缺失、controller.onEnd 等待边界缺失,或记录仍包含 TBD/待确认/未确认/用户确认中</fails_when>
</verify>
<acceptance_criteria>
- 八个 command 的稳定 top-level enum code、注册顺序、CoreState/runner 访问方式和 pathfinding 等待边界均有完整确认记录 (D-08, D-25)
</acceptance_criteria>
<resume-signal>回复 confirm-record、edit-record或直接修改契约文件后回复 approved</resume-signal>
<done>Replay command 公共边界已确认,后续实现不需要猜测稳定编号或访问入口。</done>
</task>
<task type="auto">
<name>Task 2: async-safe replay decorators 与模块默认 command items</name>
<files>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</files>
<read_first>.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</read_first>
<action>按 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)。</action>
<verify>
<automated>pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts</automated>
<fails_when>测试非零退出、任何异步 action 在 Promise settle 前完成、命令顺序/code 与 checkpoint 不同、重复注册被接受、纯查询被记录或失败动作返回 true</fails_when>
</verify>
<acceptance_criteria>
- decorator 对同步/异步外部状态入口的 collection 生命周期均正确,内部/纯函数没有重复包装 (D-05, D-06)
- 八个 registration items 均存在且只描述各自模块动作,未在模块内抢占其他模块 code (D-08)
- command tests 全部通过,参数失败可观察为 false (D-07)
</acceptance_criteria>
<done>Replay decorator 与八个模块默认 command items 已有可执行、可测试的实现。</done>
</task>
<task type="auto">
<name>Task 3: CoreState 顶层注册八个稳定 replay commands</name>
<files>packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts, packages-user/data-state/src/replay/commands.test.ts</files>
<read_first>.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</read_first>
<action>在 CoreState 的最终顶层装配区创建/持有经 checkpoint 确认的 ReplaySystem并按“up/right/down/left/auto-pathfind/use-item/equip/unequip”顺序一次性注册所有 module defaultstop-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)。</action>
<verify>
<automated>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 }</automated>
<fails_when>任一命令非零退出、注册顺序不匹配、CoreState 没有独立 ReplaySystem、或数据端既有测试出现 failed</fails_when>
</verify>
<acceptance_criteria>
- fresh CoreState 每次拥有独立 replay system八个 command 按确认顺序注册 (D-09, D-25)
- top-level 是唯一稳定 code 分配点module items 只提供实现 (D-08, D-10)
- full focused data suite 仍通过 (D-04)
</acceptance_criteria>
<done>CoreState 能直接提供经确认的八项 replay command registry所有状态改变动作具备完整等待语义。</done>
</task>
</tasks>
<threat_model>
## 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. |
</threat_model>
<verification>
- 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.
</verification>
<success_criteria>
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.
</success_criteria>
<output>
Create `.planning/phases/03-data-completion/03-03-SUMMARY.md` when done
</output>

View File

@ -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"
---
<objective>
把 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.
</objective>
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/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
</context>
<tasks>
<task type="checkpoint:decision" gate="blocking-human">
<name>Task 1: 批准首分歧 thrown diagnostic 与输出字段</name>
<files>.planning/phases/03-data-completion/03-REPLAY-DIAGNOSTICS.md</files>
<read_first>.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</read_first>
<decision>批准 Node verifier 在首个 replay 分歧立即抛出 implementation-local error并在本地 diagnostic/output contract 中报告 index、top-level stable enum code、params、reason不新增 user-facing error class不改变 replay command 的 Promise&lt;boolean&gt; 公共接口</decision>
<context>D-07 与最新用户澄清已经锁定失败机制和四个报告字段;本 checkpoint 只把它们写成可测试的 verifier-local 记录,并确认参数展示使用确定性的安全表示,不把格式扩展成新的 replay API。</context>
<options>
<option id="approve-locked-contract"><name>批准锁定的 thrown diagnostic contract</name><pros>满足 D-07 且不改变 replay public types</pros><cons>verifier 必须维护本地错误包装与输出</cons></option>
<option id="edit-record"><name>编辑记录但保持锁定字段与机制</name><pros>可补充实现所需的本地输出细节</pros><cons>不得改为 logger-only、继续执行、result-object public API 或新的用户错误类</cons></option>
</options>
<action>暂停并在 `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)。</action>
<verify>
<automated>$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 }</automated>
<fails_when>记录缺失 thrown mechanism、index/code/params/reason 任一字段、top-level stable enum 约束或 public Promise&lt;boolean&gt; 保持语义,或仍含 TBD/待确认/未确认/用户确认中</fails_when>
</verify>
<acceptance_criteria>
- 首分歧机制明确为 verifier-local thrown error且立即停止 (D-07)
- 诊断字段完整包含 index、stable enum code、params、reason不改变 replay public interface (D-07, D-08)
</acceptance_criteria>
<resume-signal>回复 approve-locked-contract、edit-record或直接修改记录后回复 approved不得修改 D-07 的 thrown/first-divergence/四字段锁定语义</resume-signal>
<done>首分歧错误机制与本地输出字段已完成 blocking approvalTask 2 才可实现 verifier。</done>
</task>
<task type="tracer">
<name>Task 2: 固定闭环 fixture 与首分歧 Node verifier</name>
<files>packages-user/data-state/test/fixtures/closed-loop.ts, script/test-data-node.ts, package.json</files>
<read_first>.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</read_first>
<action>在 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.jsonrunner 直接从 `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)。
</action>
<verify>
<automated>pnpm test:data-node</automated>
<fails_when>命令非零退出、fixture 依赖随机/legacy/browser 数据、失败未在首个分歧抛错并停止、诊断缺少 index/code/params/reason、改变 replay public boolean contract、或最终比较发生在正常结束之前</fails_when>
</verify>
<acceptance_criteria>
- 固定 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)
</acceptance_criteria>
<done>固定闭环 Node verifier 可独立、确定性地成功或首分歧失败。</done>
</task>
<task type="auto">
<name>Task 3: Node replay verifier regression tests</name>
<files>packages-user/data-state/test/nodeReplay.test.ts, packages-user/data-state/test/coreNode.test.ts</files>
<read_first>.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</read_first>
<action>为 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)。</action>
<verify>
<automated>pnpm exec vitest run packages-user/data-state/test/nodeReplay.test.ts packages-user/data-state/test/coreNode.test.ts</automated>
<fails_when>测试非零退出、thrown diagnostic 缺少任一首分歧字段、失败后仍执行后续 command、最终 snapshot 比较被跳过或测试依赖 DOM/legacy global</fails_when>
</verify>
<acceptance_criteria>
- 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)
</acceptance_criteria>
<done>Node verifier 的成功、首分歧和最终快照语义均有自动回归覆盖。</done>
</task>
</tasks>
<threat_model>
## 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. |
</threat_model>
<verification>
- `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.
</verification>
<success_criteria>
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.
</success_criteria>
<output>
Create `.planning/phases/03-data-completion/03-04-SUMMARY.md` when done
</output>

View File

@ -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"
---
<objective>
完成 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.
</objective>
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/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
</context>
<tasks>
<task type="checkpoint:decision" gate="blocking-human">
<name>Task 1: 批准 common/data-common back-edge 的最小闭环契约</name>
<files>.planning/phases/03-data-completion/03-COMMON-CYCLE-CONTRACT.md</files>
<read_first>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</read_first>
<decision>在不改变四层用户-owned data contract 的前提下,批准移除 `packages/common/src/utils/types.ts:1``@user/data-common` 的反向 import并记录 `IFacedTileLocator.direction` 的兼容表示、受影响路径与 gate 判定;不得以“允许 common 基础依赖”为理由保留当前 common/data-common cycles</decision>
<context>最新 circular 输出明确有 13 条 cycle 经过 `packages/common/src/utils/types.ts:1` 与 data-common。D-20 只允许依赖已经无环的 `@motajs/common`,因此需要一个显式的低层公共类型边界决定;这是现有 common 公共接口的修复,不能由执行者无 checkpoint 猜测。</context>
<options>
<option id="numeric-compatible-field"><name>将 common 侧 `IFacedTileLocator.direction` 改为不依赖 data-common 的数值兼容表示</name><pros>只修改 common back-edge 所在文件,不新增跨层导出符号,现有 FaceDirection 数值调用方保持可用</pros><cons>需要用户确认 common 侧既有接口的兼容表示</cons></option>
</options>
<action>暂停并在 `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。</action>
<verify>
<automated>$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 }</automated>
<fails_when>契约记录缺失精确 common back-edge 路径、13 条 cycle 的失败判定、兼容表示、D-20 约束或用户批准结果,或仍含未决占位语义</fails_when>
</verify>
<resume-signal>回复 numeric-compatible-field或直接修改契约文件后回复 approved不得批准保留当前 common/data-common cycles</resume-signal>
<done>common/data-common cycle closure 的公共边界与精确修改路径已完成 blocking approval。</done>
</task>
<task type="auto">
<name>Task 2: 闭合 common/data-common 与剩余数据包循环并建立 scoped gates</name>
<files>packages/common/src/utils/types.ts, script/check-data-circular.ts, script/check-data-type.ts</files>
<read_first>.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</read_first>
<action>严格按已批准的 `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)。</action>
<verify>
<automated>pnpm exec tsx script/check-data-circular.ts; if ($?) { pnpm exec tsx script/check-data-type.ts }</automated>
<fails_when>common/data-common back-edge 或任一四包内部/互边界 cycle 被漏报、Plan 03-01 的两条 singleton/barrel back-edge 重新出现、四包类型诊断被错误归类,或任一 scoped checker 非零</fails_when>
</verify>
<acceptance_criteria>
- 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)
</acceptance_criteria>
<done>common/data-common back-edge、四包 mutual-boundary cycles 与 Wave 1 singleton/barrel back-edges 均有明确闭环实现和可重复 gate。</done>
</task>
<task type="auto">
<name>Task 3: 补齐 DATA-01 focused closure 并执行最终门禁</name>
<files>packages-user/data-state/test/dataClosure.test.ts</files>
<read_first>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</read_first>
<action>使用显式 fake/state fixture 和现有公开接口,分别提供 focused assertionsenemy manager 的创建/属性或保存行为Flag 的设置/读取与 save/loadcombat 的确定性伤害结果saveable content 的 saveState/loadState round triptrigger/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 诊断伪装成全仓清洁。</action>
<verify>
<automated>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 }</automated>
<fails_when>任一数据 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 六类行为</fails_when>
</verify>
<acceptance_criteria>
- `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)
</acceptance_criteria>
<done>Phase 3 的数据测试、Node replay、四包类型和包含 common/data-common 边界的循环门禁均有可重复绿色证据。</done>
</task>
</tasks>
<threat_model>
## 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. |
</threat_model>
<verification>
- `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
</verification>
<success_criteria>
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.
</success_criteria>
<output>
Create `.planning/phases/03-data-completion/03-05-SUMMARY.md` when done
</output>

View File

@ -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"
---
<objective>
按用户最新 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.
</objective>
<execution_context>
@C:/Users/book/.config/opencode/gsd-core/workflows/execute-plan.md
@C:/Users/book/.config/opencode/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/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
</context>
<tasks>
<task type="tracer">
<name>Task 1: Tile events raw-data 到 accessor tracer</name>
<files>packages-user/data-common/src/store/tileStore.ts, packages-user/data-common/src/store/tileStore.test.ts</files>
<read_first>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</read_first>
<action>严格以当前 `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)。</action>
<verify>
<automated>$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</automated>
<fails_when>测试非零、输出 failed、getEvent 返回值不是 events map、缺失 tile 不是安全空 map、重复 tile/index 行为回归,或实现重新读取 trigger 标量</fails_when>
</verify>
<acceptance_criteria>
- TileStore 与工作区现有 `ITileRawData.events`/`ITileStore.getEvent()` 完全一致 (D-22)
- raw events map 到 accessor 的单路径和缺失/重复索引边界均有 focused assertions (D-01, D-04)
</acceptance_criteria>
<done>TileStore 已按 events-map contract 工作focused accessor tests 通过。</done>
</task>
<task type="auto">
<name>Task 2: Legacy tile conversion events-map regression</name>
<files>packages-user/data-state/src/legacy/tile.ts, packages-user/data-state/test/tileLegacy.test.ts</files>
<read_first>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</read_first>
<action>调整 `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)。</action>
<verify>
<automated>$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</automated>
<fails_when>任一 `it` 调用没有前置中文单行覆盖注释、任一测试非零、legacy conversion 输出不是合法 events map、Node 测试依赖 main/window/document 全局,或 Phase 5 legacy-only 文件被扩大修改</fails_when>
</verify>
<acceptance_criteria>
- 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)
</acceptance_criteria>
<done>Tile implementation 与必要 legacy conversion 已迁移到用户锁定 contractPlan 01/04/05 可消费统一结果。</done>
</task>
</tasks>
<threat_model>
## 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. |
</threat_model>
<verification>
- `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.
</verification>
<success_criteria>
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.
</success_criteria>
<output>
Create `.planning/phases/03-data-completion/03-06-SUMMARY.md` when done
</output>

View File

@ -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>
## 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 L0L3 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<boolean>`, 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<number,
The implementation source instead returns a scalar `trigger` and reads `data.trigger`; `TileLegacyBridge` also returns `trigger: -1`. [VERIFIED: packages-user/data-common/src/store/tileStore.ts:23-29; packages-user/data-state/src/legacy/tile.ts:96-105]
The current `pnpm check:type` run reports this exact mismatch plus unrelated client/legacy errors. This is a hard planning checkpoint: do not edit the user-owned interface, change the implementation semantics, or widen the type until the user identifies the intended contract. [VERIFIED: `pnpm check:type` run on 2026-09-10; packages-user/data-common/src/store/types.ts:47-86; packages-user/data-common/src/store/tileStore.ts:23-29]
The current `pnpm check:type` run reports this exact mismatch plus unrelated client/legacy errors. The latest user decision makes the current `ITileRawData.events`/`getEvent()` contract authoritative, so implementation and legacy conversion must migrate to it without editing the user-owned interface or restoring the scalar trigger shape. [VERIFIED: `pnpm check:type` run on 2026-09-10; packages-user/data-common/src/store/types.ts:47-86; packages-user/data-state/src/legacy/tile.ts:96-105; .planning/phases/03-data-completion/03-CONTEXT.md:45-47]
### CoreState and singleton wiring
@ -238,17 +252,17 @@ The replay safety decorators are separate from command recording: `shouldReplay`
The following is a planning decomposition, not authorization to change interfaces. [ASSUMED]
### Wave 0 — Contract checkpoint (blocking)
### Wave 0 — Locked contract records (blocking execution gates)
Ask the user to resolve: factory signature and return shape; replay ownership/registration location; stable command-code allocation; first-divergence reporting shape; fixed fixture encoding and exact snapshot fields; minimal built-in names/signatures; Node save/Dexie behavior; the `ITileRawData`/`ITileStore` mismatch; and whether transitive `@motajs/common` cycles/logger are in the Phase 3 repair scope. [ASSUMED]
Record the already-resolved factory, legacy boundary, replay, diagnostic, event, save/snapshot, Tile, and circular-scope decisions in the phase contract files before implementing their consumers. These checkpoints verify completeness and prevent accidental contract drift; they are not open design questions. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:43-52]
### Wave 1 — Baseline and package gates
Capture the existing type, circular, and data-test outputs; repair only implementation-side errors after the contract checkpoint; add a four-package circular command that traverses the approved boundary; and keep unrelated client/render diagnostics outside the Phase 3 gate. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:36-41; current command results above]
Capture the existing type, circular, and data-test outputs; repair only implementation-side errors inside the approved four-package boundary; add a four-package circular command that traverses the approved mutual boundary; and keep unrelated client/render diagnostics outside the Phase 3 gate. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:36-45; current command results above]
### Wave 2 — Node-safe factory and legacy boundary
Extract construction from the singleton, inject legacy conversion dependencies, and ensure importing the Node data entry does not evaluate `window`, `document`, `main`, render hooks, or browser-only loading paths. Preserve the existing singleton only as an explicitly confirmed compatibility adapter. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:30-35; packages/common/src/logger.ts:24-40; packages-user/data-base/src/game.ts:24-37,72-93; packages-user/entry-data/src/mota.ts:116-154]
Keep the no-argument factory and constructor initialization, inject legacy conversion dependencies through an internal boundary rather than direct globals, and ensure importing the Node data entry does not evaluate `window`, `document`, `main`, render hooks, or browser-only loading paths. Preserve the existing singleton only as a compatibility adapter. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:30-35,43-52; packages/common/src/logger.ts:24-40; packages-user/data-base/src/game.ts:24-37,72-93; packages-user/entry-data/src/mota.ts:116-154]
### Wave 3 — Replay registration and state-changing entrances
@ -256,7 +270,7 @@ Instantiate/register the replay system at the approved top level, add only the c
### Wave 4 — Minimal event built-ins and fixed closure fixture
Register only the user-approved built-ins, create a deterministic map/event fixture, play at least one player action through the existing event dispatch path, and compare the approved exact snapshots after each required boundary and at normal end. Do not infer a complete legacy event catalog. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:24-41; packages-user/data-system/src/event/system.ts:11-18; packages-user/data-state/src/hero/moverImpl.ts:79-135]
Register only the user-approved built-ins, create a deterministic map/event fixture, play at least one player action through the existing event dispatch path, and compare the approved full hero attributes and every map matrix only at normal end. Per-step hero checks may remain focused-test diagnostics; do not infer a complete legacy event catalog. [VERIFIED: .planning/phases/03-data-completion/03-CONTEXT.md:24-41,48-52; packages-user/data-system/src/event/system.ts:11-18; packages-user/data-state/src/hero/moverImpl.ts:79-135]
### Wave 5 — Dedicated Node command and final gates
@ -264,20 +278,20 @@ Add the user-approved Node verifier as a separate non-watch command. It must cre
## Concrete Verification Commands
The first two commands are current repository commands; the Node command name remains a user decision. [VERIFIED: package.json:6-24]
The data suite is the current repository command; the dedicated Node command and scoped gates are fixed by D-16/D-21. [VERIFIED: package.json:6-24; .planning/phases/03-data-completion/03-CONTEXT.md:36-52]
```bash
pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state
pnpm check:type
pnpm check:circular
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
pnpm exec <approved-phase-3-node-replay-command>
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<string, unknown>`. [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<string, unknown>`. [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 <approved-phase-3-node-replay-command>` | ❌ 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 L0L3 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<boolean>`, 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

View File

@ -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 `<automated>` 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