docs(phase-3): complete phase transition

This commit is contained in:
unanmed 2026-09-10 21:17:20 +08:00
parent 0d657cae6b
commit e9198a4b69
7 changed files with 709 additions and 31 deletions

View File

@ -16,7 +16,7 @@
- ✓ `Mota` 模块注册表(运行时 DI 容器)— 现有
- ✓ 数据端 / 渲染端双端分离(数据端可在 Node 中独立跑回放验证)— 现有
- ✓ 渲染引擎WebGL2 + 自定义 Vue renderer— 已重构完成
- ✓ 数据层 L0L3data-common / data-base / data-system / data-state接口设计进行中
- ✓ 数据层 L0L3data-common / data-base / data-system / data-statePhase 3
- ✓ `SaveSystem`Dexie/ `ReplaySystem`(回放验证)— 现有
- ✓ 战斗伤害系统 + 触发器注册表 — 现有
- ✓ audio / animate / loader / system热键与 UI— 现有
@ -54,11 +54,13 @@
| Decision | Rationale | Outcome |
|----------|-----------|---------|
| 数据层接口设计由用户完成AI 仅实现 | 保证接口设计质量 | — Pending |
| 数据层接口设计由用户完成AI 仅实现 | 保证接口设计质量 | Phase 3 已按用户契约落地 |
| 渲染层先于数据层完成重构 | 从旧引擎逐步重构的既定顺序 | — Pending |
| 事件系统采用 blockly 式低代码,仅覆盖简单场景 | 面向初学者,避免过度设计 | — Pending |
| 引擎含编辑器,但编辑器在独立项目 | 职责边界清晰 | — Pending |
| AI 可在验证通过后自行创建 git commit | 以自动化验证替代逐次审批,降低碎片化提交成本 | — Pending |
| AI 可在验证通过后自行创建 git commit | 以自动化验证替代逐次审批,降低碎片化提交成本 | 已执行 |
| Phase 3 数据端通过独立 Node replay 与四包质量门禁验收 | 保证数据端不依赖 DOM 且边界可重复验证 | Phase 3 验证通过 |
## Evolution
@ -78,4 +80,4 @@ This document evolves at phase transitions and milestone boundaries.
4. Update Context with current state
---
*Last updated: 2026-09-07 after initialization*
*Last updated: 2026-09-10 after Phase 3*

View File

@ -15,7 +15,7 @@ Decimal phases appear between their surrounding integers in numeric order.
- [ ] **Phase 1: 事件系统** - blockly 式低代码事件定义,驱动简单场景事件流程(验证已通过,待完成阶段收尾)
- [ ] **Phase 2: 寻路系统** - 自动寻路与移动端点击地图触发移动
- [ ] **Phase 3: 数据端完成** - 数据端 L0L3 接口全部落地,可在 Node 环境独立跑回放验证
- [x] **Phase 3: 数据端完成** - 数据端 L0L3 接口全部落地,可在 Node 环境独立跑回放验证 (completed 2026-09-10)
- [ ] **Phase 4: 渲染适配与双布局** - 新数据层 ↔ 已重构渲染端对接,支持移动端与桌面端双布局
- [ ] **Phase 5: Legacy 移植** - 删除被新接口覆盖的旧系统,迁移仍需要的内容
- [ ] **Phase 6: 单元测试** - 为核心系统补齐单元测试
@ -122,9 +122,13 @@ Plans:
3. 数据端与渲染端保持双端分离,渲染相关代码经 `r()`/`rf()` 门控或走 hook渲染端不向数据端推送更新
4. 接口设计由用户主导AI 仅负责实现
**Plans**: 6/6 plans executed
**Plans**: 9/9 plans complete
Plans:
- [x] 03-07-PLAN.md
- [x] 03-08-PLAN.md
- [x] 03-09-PLAN.md
- [x] 03-01-PLAN.md — Node-safe CoreState、内部 legacy 依赖边界与最小 replay tracer
- [x] 03-02-PLAN.md — 八个事件内建函数契约 checkpoint 与最小注册实现
- [x] 03-03-PLAN.md — 稳定 replay enum、异步 command 与 top-level 注册
@ -151,7 +155,7 @@ Plans:
**Wave 5** *(blocked on Wave 4 completion)*
- [ ] 03-05-PLAN.md — DATA-01 focused closure 与四包 type/circular 最终门禁
- [x] 03-05-PLAN.md — DATA-01 focused closure 与四包 type/circular 最终门禁
### Phase 4: 渲染适配与双布局
@ -205,7 +209,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6
|-------|----------------|--------|-----------|
| 1. 事件系统 | 13/13 | In Progress| |
| 2. 寻路系统 | 5/5 | In Progress| |
| 3. 数据端完成 | 6/6 | In Progress| |
| 3. 数据端完成 | 9/9 | Complete | 2026-09-10 |
| 4. 渲染适配与双布局 | 0/TBD | Not started | - |
| 5. Legacy 移植 | 0/TBD | Not started | - |
| 6. 单元测试 | 0/TBD | Not started | - |

View File

@ -2,18 +2,18 @@
gsd_state_version: 1.0
milestone: v1.0
milestone_name: milestone
current_phase: 3
current_phase_name: data-completion
status: executing
stopped_at: Phase 3 context gathered
last_updated: "2026-09-10T07:26:53.901Z"
current_phase: 4
current_phase_name: 渲染适配与双布局
status: planning
stopped_at: Phase 3 complete, ready to plan Phase 4
last_updated: "2026-09-10T20:30:00Z"
last_activity: 2026-09-10
last_activity_desc: Phase 02 structural correction executed from user review
last_activity_desc: Phase 03 verified and completed; ready to plan Phase 04
progress:
total_phases: 3
completed_phases: 2
total_plans: 24
completed_plans: 18
total_phases: 6
completed_phases: 3
total_plans: 27
completed_plans: 27
state_head: 2eebb0ad3fc0d3acfb19a41338c26155b7134f61
---
@ -21,25 +21,25 @@ state_head: 2eebb0ad3fc0d3acfb19a41338c26155b7134f61
## Project Reference
See: .planning/PROJECT.md (updated 2026-09-07)
See: .planning/PROJECT.md (updated 2026-09-10)
**Core value:** 引擎能完整跑通一部魔塔——开局到结局,存档、战斗、地图、事件、剧情全链路可玩。
**Current focus:** Phase 3 — data-completion
**Current focus:** Phase 4 — 渲染适配与双布局
## Current Position
Phase: 3 (data-completion) — EXECUTING
Plan: 1 of 6
Status: Executing Phase 3
Last activity: 2026-09-10 — Phase 3 execution started
Phase: 4 — 渲染适配与双布局
Plan: Not started
Status: Ready to plan
Last activity: 2026-09-10 — Phase 3 complete, transitioned to Phase 4
Progress: [█████████░] 92%
Progress: [█████░░░░░] 50%
## Performance Metrics
**Velocity:**
- Total plans completed: 5
- Total plans completed: 9
- Average duration: N/A
- Total execution time: 0 hours
@ -47,7 +47,7 @@ Progress: [█████████░] 92%
| Phase | Plans | Total | Avg/Plan |
|-------|-------|-------|----------|
| - | - | - | - |
| 3 | 9 | - | - |
**Recent Trend:**
@ -125,6 +125,9 @@ Recent decisions affecting current work:
- [Phase 02]: 2026-09-10 用户结构审查修正graph 类型及注释归回 path/types.ts删除未授权的 HeroPathfinding L3 封装及其接线。
- [Phase 02]: 2026-09-10 用户结构审查修正DirectionMapper 由 IDataCommon 主对象共享注入;通行性谓词提取为 predicate.ts 的 DefaultPassPredicate。
- [Phase 02]: 2026-09-10 直接执行摘要 02-06旧的 L3 HeroPathfinding 相关验证记录仅代表历史实现,必须按修正后的 L2 范围重新验证。
- [Phase 03]: 数据端通过独立 Node replay、19 个数据测试文件和四包 type/circular 门禁验证。
- [Phase 03]: IFacedTileLocator 移入 @user/data-common移除 @motajs/common → data-common 循环依赖。
- [Phase 03]: 序列化事件注册、null-safe built-ins 与 production replay-safety wiring 通过 gap closure 验证。
### Pending Todos
@ -132,7 +135,7 @@ None yet.
### Blockers/Concerns
- 数据层 L0L3 接口设计仍在进行中由用户主导DATA-01Phase 3依赖接口设计落地
- Phase 4 渲染适配尚未开始,需在数据端 Phase 3 完成后对接新数据层接口
- 01-08 rawEvent cache-safety and no-as implementation assumptions are explicitly removed from the revised executable scope; the 01-05 current contract remains unchanged.
- eventStore circular paths are explicitly preserved as the Phase 01 baseline; the revised 01-08 regression does not require those paths to disappear.
- Plan 01-12 leaves the repository-wide type gate blocked only by pre-existing diagnostics outside the plan-owned files; these are recorded in the phase deferred-items ledger.
@ -147,6 +150,6 @@ None yet.
## Session Continuity
Last session: 2026-09-10T04:02:44.223Z
Stopped at: Phase 3 context gathered
Resume file: .planning/phases/03-data-completion/03-CONTEXT.md
Last session: 2026-09-10T20:30:00Z
Stopped at: Phase 3 complete, ready to plan Phase 4
Resume file: None

View File

@ -0,0 +1,140 @@
---
phase: 03-data-completion
plan: 07
type: execute
wave: 5
depends_on: [03-01, 03-02, 03-03, 03-04, 03-06]
files_modified:
- packages-user/data-state/src/core.ts
- packages-user/data-state/src/legacy/dependencies.ts
- packages-user/data-state/src/legacy/events.ts
- packages-user/data-state/test/fixtures/closed-loop.ts
- packages-user/data-state/test/coreSerializedEvents.test.ts
autonomous: true
requirements: [DATA-01]
gap_closure: true
gap_ids: [GAP-03-01]
estimate:
tokens: 28000
raw_tokens: 28000
tasks: 2
confidence: low
must_haves:
truths:
- "GAP-03-01: CoreState's approved internal load path registers serialized Statement[] events before map data is bound, without adding a public registration API (D-10, D-11, D-19, D-28)."
- "GAP-03-01: The fixed Node replay fixture reaches the same production registration helper and binds event-store ids through IMapRawData.events/MapState.fromRaw, so the event mutation is not manually inserted into eventStore (D-13, D-22, D-24, D-27)."
- "GAP-03-01: Event bodies remain in GameEventStore and map/tile data retain only event ids; the existing @user/data-common ownership of IFacedTileLocator and the Phase 3 data/render, legacy-migration, and Phase 6 boundaries remain unchanged."
artifacts:
- path: "packages-user/data-state/src/legacy/events.ts"
provides: "Internal serialized event definition registration using existing Statement[], EventTrigger, GameEvent, and the CoreState interpreter"
- path: "packages-user/data-state/src/core.ts"
provides: "CoreState internal load ordering that registers event definitions before map event-id binding"
- path: "packages-user/data-state/test/coreSerializedEvents.test.ts"
provides: "Regression coverage for serialized registration, trigger preservation, and coordinate event-id binding"
- path: "packages-user/data-state/test/fixtures/closed-loop.ts"
provides: "Fixed replay fixture whose event comes from serialized registration and raw map event data"
key_links:
- "Internal serialized load payload -> registerSerializedEvents -> CoreState.eventStore"
- "IMapRawData.events -> MapState.fromRaw -> LayerEventView event id -> mover/event executor"
- "registered GameEvent interpreter -> approved event built-in registration -> eventSetBlock map mutation"
---
<objective>
Close GAP-03-01 by wiring the approved serialized event registration and map-event-id binding path into the existing CoreState data-load boundary, then make the fixed Node replay fixture prove that production path rather than manually populating the event store.
Purpose: The verification report found that `core.ts` still has the deferred registration seam and that the replay fixture bypasses it. This plan closes only that source-level gap while preserving the existing id-only map/save contract, Node-safe factory, event built-in scope, `@user/data-common` IFacedTileLocator move, and all Phase 3 boundaries.
Output: An internal registration adapter, CoreState load-order wiring, a serialized-event/map-binding regression, and a production-path closed-loop fixture.
</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-VERIFICATION.md
@.planning/phases/03-data-completion/03-03-SUMMARY.md
@.planning/phases/03-data-completion/03-04-SUMMARY.md
@.planning/phases/03-data-completion/03-EVENT-CONTRACT.md
@packages-user/data-common/src/event/types.ts
@packages-user/data-common/src/event/event.ts
@packages-user/data-common/src/store/types.ts
@packages-user/data-base/src/map/mapState.ts
@packages-user/data-state/src/core.ts
@packages-user/data-state/src/legacy/dependencies.ts
@packages-user/data-state/src/event/index.ts
@packages-user/data-state/test/fixtures/closed-loop.ts
@dev.md
</context>
<tasks>
<task type="tracer">
<name>Task 1: GAP-03-01 serialized event registration and CoreState load-order tracer</name>
<gap_id>GAP-03-01</gap_id>
<files>packages-user/data-state/src/legacy/events.ts, packages-user/data-state/src/legacy/dependencies.ts, packages-user/data-state/src/core.ts</files>
<precondition>The approved serialized data source exposes event definitions as existing AnonTokyo Statement[] plus EventTrigger and exposes map event references in the existing IMapRawData.events shape; if either source is absent, stop under D-02/D-17 instead of inventing a public contract.</precondition>
<read_first>packages-user/data-state/src/core.ts, packages-user/data-state/src/legacy/dependencies.ts, packages-user/data-common/src/event/types.ts, packages-user/data-common/src/event/event.ts, packages-user/data-common/src/store/types.ts, packages-user/data-base/src/map/mapState.ts, packages-user/data-state/src/event/index.ts, .planning/phases/03-data-completion/03-EVENT-CONTRACT.md, .planning/phases/03-data-completion/03-CONTEXT.md, dev.md</read_first>
<action>Implement one internal serialized-data adapter in `legacy/events.ts` that accepts the existing event-id -> `{ trigger, rawEvent: Statement[] }` records, creates `GameEvent` instances with the already assembled `GameEventSystem` interpreter, preserves each EventTrigger and raw Statement[] contract, and registers each id in the existing `GameEventStore`. Extend only the internal `ILegacyLoadData`/explicit-load payload in `legacy/dependencies.ts` as needed to carry those definitions and the existing per-floor `IMapRawData.events` references; do not add a public registration method, new public type barrel, event bodies inside maps/saves, or a second event model. In `core.ts`, call the adapter during the existing data initialization boundary before map construction/binding, and feed the raw map event references through the existing `MapState.fromRaw` path so coordinate views retain only priority-to-event-id mappings. Add the first focused assertion in `coreSerializedEvents.test.ts` for registration lookup, trigger/raw Statement[] preservation, and coordinate id binding; place a Chinese single-line coverage comment immediately before every new `it` call as required by `dev.md:85` (D-01, D-03, D-04). Keep the no-argument `createCoreState()` and constructor Node path unchanged, keep legacy access behind the injected dependency boundary, and preserve the approved `@user/data-common` ownership of `IFacedTileLocator` (D-10, D-11, D-19, D-22, D-23, D-28). Remove the obsolete deferred initialization seam only as part of this concrete wiring; do not broaden event names beyond the eight approved Phase 3 built-ins (D-24, D-27).</action>
<verify>
<automated>$lines = Get-Content -LiteralPath "packages-user/data-state/test/coreSerializedEvents.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-state/test/coreSerializedEvents.test.ts</automated>
<fails_when>coverage-comment scan throws for any it call, Vitest exits non-zero, or the focused test output reports failed assertions for serialized registration, trigger/raw Statement[] preservation, or coordinate event-id binding</fails_when>
</verify>
<done>The internal data-load boundary creates real GameEvent instances from serialized Statement[] definitions, CoreState registers them before map-id binding, no public registration API or event-body persistence shape is added, and the focused registration test passes.</done>
</task>
<task type="auto">
<name>Task 2: GAP-03-01 production-path replay fixture and map-event binding regression</name>
<gap_id>GAP-03-01</gap_id>
<files>packages-user/data-state/test/fixtures/closed-loop.ts, packages-user/data-state/test/coreSerializedEvents.test.ts</files>
<read_first>packages-user/data-state/src/legacy/events.ts, packages-user/data-state/src/core.ts, packages-user/data-state/test/fixtures/closed-loop.ts, packages-user/data-state/test/nodeReplay.test.ts, packages-user/data-state/src/event/index.ts, packages-user/data-base/src/map/mapState.ts, packages-user/data-common/src/event/types.ts, node_modules/.pnpm/anon-tokyo@0.0.0-alpha.0/node_modules/anon-tokyo/dist/index.d.ts, .planning/phases/03-data-completion/03-VERIFICATION.md</read_first>
<action>Replace the closed-loop fixture's direct `eventStore.addEvent` setup with the internal serialized registration path from Task 1. Define the mutation event as the existing AnonTokyo Statement[] call to the approved `eventSetBlock` built-in, preserve the OnEnter trigger, and put its id in the fixture's existing raw map `events` coordinate structure so `MapState.fromRaw` performs the binding. Keep the fixture deterministic and explicit: the route still performs one approved player move, the event still mutates the event-layer matrix after the awaited action, and the existing end-only hero/all-map snapshot contract remains unchanged (D-06, D-13, D-14, D-24, D-26, D-27). Add regression assertions that the registered event retains its trigger/raw statements, the target coordinate contains the event id through the map accessor, the event executes once, and the replay completes with the same expected snapshot; place a Chinese single-line coverage comment immediately before every new `it` call as required by `dev.md:85` (D-01, D-03, D-04). Do not restore scalar tile triggers, move `IFacedTileLocator` out of `@user/data-common`, add browser globals, or expand into Phase 4/5/6 behavior.</action>
<verify>
<automated>$lines = Get-Content -LiteralPath "packages-user/data-state/test/coreSerializedEvents.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-state/test/coreSerializedEvents.test.ts packages-user/data-state/test/nodeReplay.test.ts; if ($?) { pnpm test:data-node }</automated>
<fails_when>coverage-comment scan throws, either Vitest command or pnpm test:data-node exits non-zero, output reports failed tests, replay does not reach normal end, event mutation or event-id binding is absent, or the final snapshot differs from the fixed expectation</fails_when>
</verify>
<done>The fixed Node replay no longer manually inserts the event; serialized registration, raw map event-id binding, awaited built-in mutation, normal replay end, and exact snapshots all pass through the production data-side path.</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|---|---|
| Serialized event definitions -> GameEventStore | External Statement[] and trigger data become executable event objects. |
| Raw map event references -> MapState/LayerEventView | Coordinate, priority, and id values become runtime event bindings. |
| Event interpreter -> approved built-ins | Serialized calls can request state mutations through the bounded built-in registry. |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-03-16 | Tampering | serialized event definitions | high | mitigate | Validate event ids, trigger values, and Statement[] container shape at the internal load boundary; use the existing interpreter and the eight approved built-ins only. |
| T-03-17 | Tampering | raw map event references | medium | mitigate | Reuse MapState raw validation and coordinate/priority/id checks before map registration; preserve id-only storage. |
| T-03-18 | Code execution | Node serialized event path | high | mitigate | Keep the path Node-safe and dependency-injected; do not evaluate legacy globals or introduce dynamic code compilation outside AnonTokyo. |
| 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/coreSerializedEvents.test.ts packages-user/data-state/test/nodeReplay.test.ts`
- `pnpm test:data-node`
- `pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state`
- `pnpm exec tsx script/check-data-type.ts`
- `pnpm exec tsx script/check-data-circular.ts`
</verification>
<success_criteria>
GAP-03-01 is closed when CoreState's internal serialized load path registers Statement[] events before map binding, the fixed replay fixture consumes that path without manual event-store insertion, and the focused plus Node/data quality gates remain green.
</success_criteria>
<output>
Create `.planning/phases/03-data-completion/03-07-SUMMARY.md` when done
</output>

View File

@ -0,0 +1,124 @@
---
phase: 03-data-completion
plan: 08
type: execute
wave: 3
depends_on: [03-02]
files_modified:
- packages-user/data-state/src/event/index.ts
- packages-user/data-state/src/event/event.test.ts
autonomous: true
requirements: [DATA-01]
gap_closure: true
gap_ids: [GAP-03-02]
estimate:
tokens: 14000
raw_tokens: 14000
tasks: 2
confidence: low
must_haves:
truths:
- "GAP-03-02: All eight approved event built-in registrations return a resolved void result for null and undefined runtime parameters instead of throwing (D-24, D-27)."
- "GAP-03-02: Valid parameter parsing and awaited map/hero/event behavior remain unchanged while malformed parameters are stopped at the registration boundary."
- "GAP-03-02: The regression uses the actual AnonTokyo registration functions, not only direct calls to the underlying implementation helpers."
artifacts:
- path: "packages-user/data-state/src/event/index.ts"
provides: "Runtime parameter guard shared by all eight built-in registration adapters"
- path: "packages-user/data-state/src/event/event.test.ts"
provides: "Null/undefined registration regression plus existing valid and missing-target behavior coverage"
key_links:
- "AnonTokyo BuiltInFunction.func -> non-object parameter guard -> parser/approved event implementation"
- "registered built-in null/undefined calls -> resolved void -> no state mutation or thrown TypeError"
---
<objective>
Close GAP-03-02 by making every approved event registration safely return for nullish runtime parameters and proving the behavior through the real registration table.
Purpose: The verification report found six registered built-ins throw before their existing safe-failure logic runs. This is a narrow runtime-boundary correction; it does not change any event signature, field contract, built-in name, or Phase 3 scope.
Output: A shared registration guard and focused null/undefined regression coverage for all eight approved built-ins.
</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-EVENT-CONTRACT.md
@.planning/phases/03-data-completion/03-VERIFICATION.md
@.planning/phases/03-data-completion/03-02-SUMMARY.md
@packages-user/data-state/src/event/index.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/event.test.ts
@packages-user/data-system/src/event/types.ts
@dev.md
</context>
<tasks>
<task type="tracer">
<name>Task 1: GAP-03-02 guard the registered event built-in entry seam</name>
<gap_id>GAP-03-02</gap_id>
<files>packages-user/data-state/src/event/index.ts, packages-user/data-state/src/event/event.test.ts</files>
<read_first>packages-user/data-state/src/event/index.ts, 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/event.test.ts, .planning/phases/03-data-completion/03-EVENT-CONTRACT.md, .planning/phases/03-data-completion/03-CONTEXT.md</read_first>
<action>Harden the shared `createBuiltin` registration adapter so a nullish or otherwise non-object runtime parameter returns a resolved `void` result before any parser calls `Object.getOwnPropertyDescriptor` or reads fields. Keep the existing `(param, env)` shape, `IBlockEventEnv` guard, eight stable names, underlying implementation functions, and Promise waiting semantics unchanged (D-24, D-27). Add one focused regression through the actual `createEventBuiltinRegistrations()` table for `eventSetBlock` with a null parameter, asserting that the returned value is safely awaitable and no exception escapes; retain the existing valid behavior assertions and Chinese coverage comments required by `dev.md`.
<verify>
<automated>pnpm exec vitest run packages-user/data-state/src/event/event.test.ts</automated>
<fails_when>Vitest exits non-zero or reports a failed assertion, any null registration throws instead of resolving, or the focused event test file is missing</fails_when>
</verify>
<done>The registered `eventSetBlock` path safely resolves for a null runtime parameter while valid parsing and mutation behavior remain green.</done>
</task>
<task type="auto">
<name>Task 2: GAP-03-02 nullish regression matrix for all eight registrations</name>
<gap_id>GAP-03-02</gap_id>
<files>packages-user/data-state/src/event/event.test.ts</files>
<read_first>packages-user/data-state/src/event/index.ts, packages-user/data-state/src/event/event.test.ts, packages-user/data-state/src/event/types.ts, .planning/phases/03-data-completion/03-VERIFICATION.md, dev.md</read_first>
<action>Extend the focused event test to invoke every approved registration (`eventSetBlock`, `eventMoveBlock`, `eventDeleteBlock`, `eventMoveHero`, `eventMoveHeroStep`, `eventTouchFront`, `eventInsertEvents`, and `eventInsertEvent`) with both null and undefined runtime parameters through their `BuiltInFunction.func` values. Assert each invocation resolves to `undefined`, does not mutate the fixture, and does not throw; keep the existing valid behavior, missing-target behavior, exact registration order, and async-await assertions. Use an explicit runtime invocation helper rather than weakening the public parameter interfaces or adding an `any` escape, and add the required Chinese line before each new `it` call (D-01, D-03, D-04, D-24, D-27).
<verify>
<automated>pnpm exec vitest run packages-user/data-state/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts; if ($?) { pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state }</automated>
<fails_when>Either Vitest or the four-package data suite exits non-zero, output reports failed tests, any approved registration throws or mutates state for nullish input, or valid event behavior/regression coverage is absent</fails_when>
</verify>
<done>All eight real registered built-ins safely resolve for null and undefined parameters, the six previously throwing paths are covered, and the focused/full data suites remain green.</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|---|---|
| AnonTokyo runtime parameter -> built-in adapter | Untrusted serialized parameters enter the event registration functions. |
| Built-in adapter -> map/hero/event mutation | Only validated object parameters may reach the approved state-changing implementation. |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-03-19 | Denial of service | nullish/malformed built-in parameters | medium | mitigate | Guard runtime values before property access and assert all eight registrations resolve safely. |
| T-03-20 | Tampering | event parameter parser | medium | mitigate | Preserve existing field/type parsers and prevent malformed input from reaching mutation helpers. |
| 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/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts`
- `pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state`
- `pnpm exec tsx script/check-data-type.ts`
- `pnpm exec tsx script/check-data-circular.ts`
</verification>
<success_criteria>
GAP-03-02 is closed when the real eight-entry registration table safely resolves for both null and undefined parameters, valid event behavior is unchanged, and the data-side gates pass.
</success_criteria>
<output>
Create `.planning/phases/03-data-completion/03-08-SUMMARY.md` when done
</output>

View File

@ -0,0 +1,129 @@
---
phase: 03-data-completion
plan: 09
type: execute
wave: 4
depends_on: [03-03]
files_modified:
- packages-user/data-state/src/replay/commands.ts
- packages-user/data-state/src/replay/commands.test.ts
autonomous: true
requirements: [DATA-01]
gap_closure: true
gap_ids: [GAP-03-03]
estimate:
tokens: 22000
raw_tokens: 22000
tasks: 2
confidence: low
must_haves:
truths:
- "GAP-03-03: Production replay command entrances use the existing replay-safety decorator mechanism; the behavior is no longer limited to test-local wrapper functions (D-05)."
- "GAP-03-03: Decorated production actions preserve replay-safety collection through complete asynchronous movement/pathfinding Promise settlement (D-06)."
- "GAP-03-03: Pure validation, path queries, slot resolution, and registration helpers remain undecorated, so the production wiring does not create duplicate replay-safety entries (D-05)."
artifacts:
- path: "packages-user/data-state/src/replay/commands.ts"
provides: "Internal production command-entry object with replay-safety decoration at state-changing boundaries"
- path: "packages-user/data-state/src/replay/commands.test.ts"
provides: "Regression coverage proving production command decoration, async settlement, and undecorated pure paths"
key_links:
- "CoreState replay registry -> production command-entry methods -> existing hero/path/item/equipment state APIs"
- "shouldReplay production wrapper -> Promise settlement -> replay safety collection restoration"
- "pure command validation/path query -> no replay-safety collection message"
---
<objective>
Close GAP-03-03 by wiring replay-safety decoration into the production state-changing command entrances and proving the D-05 boundary with real command registry calls.
Purpose: The verification report found that `shouldReplay` and `ignoreReplay` exist only as definitions and test-local wrappers. This plan adds the approved decoration at the replay command boundary, where movement, pathfinding, item, and equipment actions enter production state mutation, without decorating pure helpers or changing the replay command/public interface.
Output: Production command-entry decoration and focused regression coverage for decorated async actions and undecorated pure paths.
</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-REPLAY-CONTRACT.md
@.planning/phases/03-data-completion/03-VERIFICATION.md
@.planning/phases/03-data-completion/03-03-SUMMARY.md
@packages-user/data-common/src/replay/func.ts
@packages-user/data-state/src/replay/commands.ts
@packages-user/data-state/src/replay/commands.test.ts
@packages-user/data-state/src/replay/types.ts
@packages-user/data-system/src/path/system.ts
@packages-user/data-base/src/hero/items.ts
@packages-user/data-base/src/hero/equipment.ts
@dev.md
</context>
<tasks>
<task type="tracer">
<name>Task 1: GAP-03-03 decorate production replay command entrances</name>
<gap_id>GAP-03-03</gap_id>
<files>packages-user/data-state/src/replay/commands.ts</files>
<read_first>packages-user/data-state/src/replay/commands.ts, packages-user/data-state/src/replay/types.ts, packages-user/data-common/src/replay/func.ts, packages-user/data-common/src/replay/types.ts, packages-user/data-system/src/path/system.ts, packages-user/data-base/src/hero/items.ts, packages-user/data-base/src/hero/equipment.ts, .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md, .planning/phases/03-data-completion/03-CONTEXT.md</read_first>
<action>Create an internal production command-entry class or equivalent class-method seam inside `commands.ts` and move the existing state-changing command bodies behind five named entrances: directional hero movement, auto-pathfinding, item use, equipment, and unequipment. Apply the existing `shouldReplay` mechanism to those five entrances with stable diagnostic messages, then have `createReplayCommandItems()` delegate to them; the four directional codes may share one decorated movement entrance. Keep `IReplayCommand.execute(): Promise<boolean>`, stable enum values/order, parameter validation, failure booleans, and controller awaiting unchanged (D-05, D-06, D-08, D-25). Leave `isNumber`, `isItem`, `isSlot`, `resolveSlot`, path queries, pure calculations, command-item construction, and top-level registration helpers outside the decoration boundary; do not add `ignoreReplay` merely to suppress a real state-changing action and do not decorate both an outer entrance and its internal state helper.
<verify>
<automated>pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts</automated>
<fails_when>Vitest exits non-zero or reports a failed assertion, production command entries are not decorated, async collection ends before Promise settlement, or pure paths create replay-safety records</fails_when>
</verify>
<done>Real production replay command items route through decorated state-changing entrances while pure validation/query helpers and stable registry assembly remain undecorated and the existing command tests pass.</done>
</task>
<task type="auto">
<name>Task 2: GAP-03-03 production decoration and async-boundary regression</name>
<gap_id>GAP-03-03</gap_id>
<files>packages-user/data-state/src/replay/commands.test.ts</files>
<read_first>packages-user/data-state/src/replay/commands.ts, packages-user/data-state/src/replay/commands.test.ts, packages-user/data-common/src/replay/func.ts, packages-user/data-common/src/replay/system.ts, .planning/phases/03-data-completion/03-VERIFICATION.md, .planning/phases/03-data-completion/03-CONTEXT.md</read_first>
<action>Add focused tests that begin a real `ReplaySystem` safety collection and invoke production `createReplayCommandItems()` entries rather than a test-local decorated function. Use a deferred movement controller to prove the production decoration keeps its nested collection context until `onEnd` settles, then assert the collected message identifies the state-changing entrance. Exercise the item/equipment command entrances as production paths, and call a pure path query/validation route while collection is active to prove it does not create a replay-safety entry. Preserve the existing test-local decorator lifecycle tests as implementation coverage, add Chinese coverage comments before new `it` calls, restore logger/console spies, and do not change route recording, command codes, public interfaces, or Phase 4/5/6 scope (D-01, D-03, D-04, D-05, D-06, D-07, D-08).
<verify>
<automated>pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts; if ($?) { pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state }</automated>
<fails_when>Either Vitest or the four-package data suite exits non-zero, output reports failed tests, deferred production actions lose collection context before settlement, pure/query paths record entries, or command behavior/regression assertions are missing</fails_when>
</verify>
<done>Production command calls, not only test wrappers, prove replay-safety decoration, Promise-settlement timing, and the exclusion of pure/internal paths; the focused and full data suites remain green.</done>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
|---|---|
| Replay route -> command entrance | Route parameters select externally callable state-changing actions. |
| Decorated action -> replay safety collection | Async completion determines whether nested state changes are fully observed. |
| Pure query/helper -> collection | Non-mutating operations must not manufacture replay-safety records. |
## STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-03-21 | Tampering | replay command entrance selection | high | mitigate | Decorate only the five production state-changing entrances and retain the stable top-level registry/parameter checks. |
| T-03-22 | Denial of service | async replay-safety lifecycle | medium | mitigate | Reuse `shouldReplay` Promise settlement behavior and assert deferred movement collection boundaries. |
| T-03-23 | Repudiation | missing or duplicate safety records | medium | mitigate | Test real registry calls plus pure-query exclusion and retain one decoration boundary per action. |
| 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/src/replay/commands.test.ts`
- `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`
</verification>
<success_criteria>
GAP-03-03 is closed when production replay command entrances use the approved replay-safety decoration, asynchronous collection lasts through action completion, pure/internal paths remain outside the boundary, and all data-side verification gates pass.
</success_criteria>
<output>
Create `.planning/phases/03-data-completion/03-09-SUMMARY.md` when done
</output>

View File

@ -0,0 +1,276 @@
---
phase: 03-data-completion
verified: 2026-09-10T13:09:33Z
status: passed
score: 26/26 must-haves verified
covered_files:
- .planning/REQUIREMENTS.md
- .planning/ROADMAP.md
- .planning/STATE.md
- .planning/phases/03-data-completion/03-01-PLAN.md
- .planning/phases/03-data-completion/03-01-SUMMARY.md
- .planning/phases/03-data-completion/03-02-PLAN.md
- .planning/phases/03-data-completion/03-02-SUMMARY.md
- .planning/phases/03-data-completion/03-03-PLAN.md
- .planning/phases/03-data-completion/03-03-SUMMARY.md
- .planning/phases/03-data-completion/03-04-PLAN.md
- .planning/phases/03-data-completion/03-04-SUMMARY.md
- .planning/phases/03-data-completion/03-05-PLAN.md
- .planning/phases/03-data-completion/03-05-SUMMARY.md
- .planning/phases/03-data-completion/03-06-PLAN.md
- .planning/phases/03-data-completion/03-06-SUMMARY.md
- .planning/phases/03-data-completion/03-07-PLAN.md
- .planning/phases/03-data-completion/03-07-SUMMARY.md
- .planning/phases/03-data-completion/03-08-PLAN.md
- .planning/phases/03-data-completion/03-08-SUMMARY.md
- .planning/phases/03-data-completion/03-09-PLAN.md
- .planning/phases/03-data-completion/03-09-SUMMARY.md
- .planning/phases/03-data-completion/03-COMMON-CYCLE-CONTRACT.md
- .planning/phases/03-data-completion/03-EVENT-CONTRACT.md
- .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md
- .planning/phases/03-data-completion/03-REPLAY-DIAGNOSTICS.md
- .planning/phases/03-data-completion/deferred-items.md
- package.json
- packages-user/data-base/src/game.ts
- packages-user/data-base/src/hero/follower.ts
- packages-user/data-base/src/hero/location.ts
- packages-user/data-base/src/hero/mover.ts
- packages-user/data-base/src/hero/state.ts
- packages-user/data-base/src/hero/types.ts
- packages-user/data-base/src/map/mapLayer.ts
- packages-user/data-common/src/common/face.ts
- packages-user/data-common/src/common/types.ts
- packages-user/data-common/src/replay/array.ts
- packages-user/data-common/src/replay/func.ts
- packages-user/data-common/src/replay/index.ts
- packages-user/data-common/src/replay/sandbox.ts
- packages-user/data-common/src/save/index.ts
- packages-user/data-common/src/save/memory.ts
- packages-user/data-common/src/store/tileStore.test.ts
- packages-user/data-common/src/store/tileStore.ts
- packages-user/data-common/src/store/types.ts
- packages-user/data-state/src/core.ts
- packages-user/data-state/src/enemy/calculator.ts
- packages-user/data-state/src/event/event.test.ts
- packages-user/data-state/src/event/event.ts
- packages-user/data-state/src/event/hero.ts
- packages-user/data-state/src/event/index.ts
- packages-user/data-state/src/event/map.ts
- packages-user/data-state/src/event/types.ts
- packages-user/data-state/src/index.ts
- packages-user/data-state/src/legacy/dependencies.ts
- packages-user/data-state/src/legacy/events.ts
- packages-user/data-state/src/legacy/move.ts
- packages-user/data-state/src/legacy/tile.ts
- packages-user/data-state/src/replay/commands.test.ts
- packages-user/data-state/src/replay/commands.ts
- packages-user/data-state/src/replay/index.ts
- packages-user/data-state/src/replay/types.ts
- packages-user/data-state/test/coreNode.test.ts
- packages-user/data-state/test/coreSerializedEvents.test.ts
- packages-user/data-state/test/dataClosure.test.ts
- packages-user/data-state/test/fixtures/closed-loop.ts
- packages-user/data-state/test/nodeReplay.test.ts
- packages-user/data-state/test/nodeTracer.test.ts
- packages-user/data-state/test/replayVerifier.ts
- packages-user/data-state/test/tileLegacy.test.ts
- packages-user/data-system/src/event/system.ts
- packages/common/src/logger.ts
- packages/common/src/utils/types.ts
- script/check-data-circular.ts
- script/check-data-type.ts
- script/test-data-node.ts
covered_digest: "v1:sha256:f3d5c0e554d5767552f375687aba39b7b87528151d761c44614d7e4bfc72a336"
behavior_unverified: 0
overrides_applied: 0
re_verification:
previous_status: gaps_found
previous_score: 23/26
gaps_closed:
- "Production serialized event registration and map event-id binding"
- "Nullish safety for all eight registered event built-ins"
- "Production replay-safety decoration at state-changing entrances"
gaps_remaining: []
regressions: []
---
# Phase 3: 数据端完成 Verification Report
**Phase Goal:** 数据端 L0L3 接口实现完成,数据层各系统可用并可在 Node 环境独立运行回放验证
**Verified:** 2026-09-10T13:09:33Z
**Status:** passed
**Re-verification:** Yes — after gap-closure plans 03-07, 03-08, and 03-09
## Goal Achievement
### Observable Truths
| # | Truth | Status | Evidence |
|---:|---|---|---|
| R1 | 用户设计的 L0L3 接口及地图、角色、敌人、flag、战斗、触发器、存档、回放系统可用 | ✓ VERIFIED | DATA-01 closure tests, serialized-event production-path tests, the 19-file/105-test data regression, and all scoped gates pass. |
| R2 | 数据端可在无 DOM 的 Node 环境独立运行回放验证 | ✓ VERIFIED | `pnpm test:data-node` exits 0 with `Node replay verifier passed`; `coreNode.test.ts` covers independent factory instances. |
| R3 | 数据端与渲染端保持双端分离 | ✓ VERIFIED | Data code has no direct client/render imports except the pre-existing legacy bridge path; that path is runtime-gated by `Mota.r()`/`Mota.require()`. The data packages do not push render updates. |
| R4 | 接口决策由用户确认AI 仅实现 | ✓ VERIFIED | Decision-coverage gate reports 28/28 honored; the four approved contract records contain no unresolved placeholders. |
| P01.1 | Node can create two independent CoreState instances without browser/IndexedDB globals | ✓ VERIFIED | `createCoreState()` directly returns `new CoreState()`; `coreNode.test.ts` and the independent Node path pass. |
| P01.2 | Fixed replay movement awaits event mutation and normal replay end | ✓ VERIFIED | `nodeTracer.test.ts`, `coreSerializedEvents.test.ts`, and the Node verifier pass. |
| P01.3 | CoreState reaches legacy data only through the injected internal boundary | ✓ VERIFIED | `core.ts:124-125, 238-240` consumes `createLegacyDependencies()` and does not read legacy globals directly. |
| P01.4 | Node uses memory save while browser compatibility remains available | ✓ VERIFIED | `dependencies.ts:46-86` selects `MemorySaveSystem` without the host and retains the legacy `SaveSystem` branch. |
| P02.1 | All eight approved built-ins safely resolve void for null and undefined parameters | ✓ VERIFIED | `event/index.ts:126-131` guards the registration seam before parsing; `event.test.ts:239-259` invokes every real registration with both nullish values and asserts resolved `undefined`, no mutation. |
| P02.2 | Built-in behavior stays within the approved map/hero/event closure | ✓ VERIFIED | `event/index.ts:135-195` contains exactly the eight approved registrations; valid behavior tests pass. |
| P02.3 | Event registrations are assembled through CoreState/GameEventSystem | ✓ VERIFIED | `core.ts:222-227` passes `createEventBuiltinRegistrations()` to `GameEventSystem`; registration tests pass. |
| P03.1 | Replay safety is applied only to externally callable state-changing entrances | ✓ VERIFIED | `commands.ts:66-170` decorates exactly movement, pathfinding, item, equip, and unequip entrances; validation, slot resolution, queries, and registry assembly remain outside. |
| P03.2 | Decorated async actions retain collection through Promise settlement | ✓ VERIFIED | `func.ts:186-195` restores context only on Promise fulfillment/rejection; production movement test uses a deferred controller and passes. |
| P03.3 | Top-level registry contains exactly the eight approved ordered commands | ✓ VERIFIED | `commands.ts:184-276` emits eight stable entries and `commands.test.ts` verifies order and duplicate rejection. |
| P03.4 | Commands await complete actions and return false on failure | ✓ VERIFIED | `commands.ts:125-169, 207-273` awaits controllers and returns explicit false for invalid/unavailable actions; command tests pass. |
| P04.1 | Fixed fixture covers initialization, player replay, state/event change, and normal end | ✓ VERIFIED | `closed-loop.ts:84-153` defines serialized statements and raw map event ids; Node replay and focused tests pass. |
| P04.2 | First replay divergence throws a local diagnostic with index/code/params/reason | ✓ VERIFIED | `nodeReplay.test.ts` covers unknown, false, throw, and snapshot divergence; the dedicated Node runner passes on the normal route. |
| P04.3 | Final comparison is exact and only after normal replay end | ✓ VERIFIED | `script/test-data-node.ts:95-115` waits for `sandbox.ended` before comparing structured hero and every map/layer matrix. |
| P04.4 | `pnpm test:data-node` is an independent non-zero process gate | ✓ VERIFIED | `package.json:10` invokes `tsx script/test-data-node.ts`; the independently launched process exits 0. |
| P05.1 | Four data packages have zero in-scope TypeScript diagnostics | ✓ VERIFIED | `pnpm exec tsx script/check-data-type.ts`: 27 total, 0 in-scope, 27 outside-scope; exit 0. |
| P05.2 | Four packages and the transitive common boundary have zero cycles | ✓ VERIFIED | `pnpm exec tsx script/check-data-circular.ts`: 0 total, 0 in-scope, 0 outside-scope cycles; exit 0. |
| P05.3 | DATA-01 closure covers enemy/Flag/combat/save-load/trigger-event/replay | ✓ VERIFIED | `dataClosure.test.ts` has six active value/behavior tests and passes in the full data regression. |
| P05.4 | Data suite, Node verifier, type gate, and circular gate are repeatable non-watch commands | ✓ VERIFIED | Sequential full Phase 3 data gate passes with 19 files and 105 tests; all three independent gates pass. |
| P06.1 | Tile runtime consumes `events` and exposes defensive `getEvent(num)` | ✓ VERIFIED | TileStore tests pass and verify raw events, lookup, replacement, missing tiles, and mutation isolation. |
| P06.2 | Legacy conversion produces the same events-map contract without scalar trigger | ✓ VERIFIED | `tileLegacy.test.ts` and TileStore integration pass; conversion emits `events` only. |
| P06.3 | Tile tests are focused and separate from full legacy migration | ✓ VERIFIED | Both Tile test files have explicit fixtures and Chinese coverage comments; no disabled tests or Phase 5 expansion is present. |
**Score:** 26/26 truths verified (0 present-but-behavior-unverified).
### Prior Blocker Closure
1. **Serialized event registration/map binding — CLOSED.** `ILegacyLoadData.serialized` enters `CoreState.initLegacyData()` before raw map construction. The symbol-keyed `LOAD_SERIALIZED_DATA` path calls `registerSerializedEvents()` and then `MapState.fromRaw()`. The fixed fixture supplies `Statement[]` plus `IMapRawData.events`; it no longer calls `eventStore.addEvent()` directly. `coreSerializedEvents.test.ts` proves trigger/raw statement preservation, coordinate id binding, one execution, and the final mutation.
2. **Nullish event built-ins — CLOSED.** `createBuiltin()` rejects null, undefined, and other non-object parameters before any `Object.getOwnPropertyDescriptor()` call. The regression invokes all eight actual registration functions for both null and undefined and checks resolved void plus unchanged state.
3. **Replay-safety production wiring — CLOSED.** `ReplayCommandEntrances` applies `shouldReplay()` to the five state-changing entrances used by the real registry. Deferred movement proves the collection remains active through `controller.onEnd`; item/equipment production calls produce safety records; pure path queries and invalid validation produce none.
## Plan/Summary Reconciliation
The nine Phase 3 PLAN/SUMMARY pairs were read. The prior SUMMARY claims were not accepted as evidence; the current source, focused tests, and independent commands were checked. Plans 03-07, 03-08, and 03-09 now match the implementation and close all three prior gaps. The ROADMAP file still has stale phase metadata (`6/6` and an unchecked 03-05 entry) while the actual phase directory contains nine plans; this is planning metadata drift, not a code or acceptance gap, and was not modified per instruction.
## Required Artifacts
| Artifact group | Level 1 existence | Level 2 substance | Level 3 wiring | Status |
|---|---|---|---|---|
| 03-01 Node CoreState, memory save, dependency boundary, tracer | Present | Concrete Node-safe construction and replay fixture | Factory → CoreState → injected dependencies → tests | ✓ VERIFIED |
| 03-02 event contracts, eight implementations, registrations, tests | Present | Eight bounded handlers with awaited behavior and safe target handling | data-state registrations → GameEventSystem → CoreState | ✓ VERIFIED |
| 03-03 replay contract, commands, decorator, registry tests | Present | Stable enum, explicit failures, Promise-aware decoration | CoreState → ReplaySystem → command entrances → state APIs | ✓ VERIFIED |
| 03-04 fixed fixture, Node runner, verifier, diagnostics | Present | Deterministic route and end-only exact snapshots | package script → Node runner → verifier harness → live state | ✓ VERIFIED |
| 03-05 scoped gates and DATA-01 closure | Present | Real `vue-tsc`/Madge classifiers and six value/behavior tests | compiler/graph output → fail-closed scope gates | ✓ VERIFIED |
| 03-06 TileStore and legacy events-map bridge | Present | Normalized, defensive event maps without scalar trigger | raw/legacy events → TileStore → `getEvent()` | ✓ VERIFIED |
| 03-07 serialized event loader and production-path fixture | Present | Validated `Statement[]` to `GameEvent` conversion and raw map binding | internal load payload → CoreState symbol path → event store/map state | ✓ VERIFIED |
| 03-08 nullish registration guard and matrix | Present | Shared runtime guard plus eight-entry null/undefined matrix | AnonTokyo `func` → guard → parser/handler | ✓ VERIFIED |
| 03-09 production replay entrances and boundary tests | Present | Five decorated state-changing entrances and pure-path exclusions | CoreState registry → decorated entrances → Promise settlement | ✓ VERIFIED |
## Key Link Verification
| From | To | Via | Status | Details |
|---|---|---|---|---|
| serialized load payload | `CoreState.eventStore` | `LOAD_SERIALIZED_DATA``registerSerializedEvents` | ✓ WIRED | `core.ts:267-282`; focused registration test passes. |
| `IMapRawData.events` | coordinate event view | `MapState.fromRaw()` | ✓ WIRED | `mapState.ts:228-243` converts raw ids into layer event views; test observes priority 10 → `mutate-map`. |
| registered `GameEvent` | event mutation | shared interpreter → `eventSetBlock` registration | ✓ WIRED | Fixture uses raw `StatementType.Call`; replay changes the event-layer matrix once. |
| all eight event registrations | safe result | `createBuiltin()` runtime object guard | ✓ WIRED | Actual `BuiltInFunction.func` values resolve null and undefined safely. |
| CoreState | event built-ins | `createEventBuiltinRegistrations()``GameEventSystem` | ✓ WIRED | Core assembly passes the module-owned registration list. |
| replay registry | state-changing entrances | `createReplayCommandItems()``ReplayCommandEntrances` | ✓ WIRED | All five production entrances are reached by real registry commands. |
| `shouldReplay` | async state collection | decorated entrance → returned Promise settlement | ✓ WIRED | Deferred movement test sees no early record and records after controller completion. |
| pure validation/path query | replay safety collection | validation before decorated call | ✓ WIRED | Pure/query regression records zero safety messages. |
| Node package command | verifier | `test:data-node` → direct `core.ts` factory | ✓ WIRED | No compatibility singleton or DOM entry is imported by the runner. |
| type gate | compiler diagnostics | `vue-tsc` output classifier | ✓ WIRED | In-scope failures are not hidden; 27 diagnostics are explicitly outside scope. |
| circular gate | dependency graph | Madge four entries plus transitive common boundary | ✓ WIRED | Gate reports zero cycles. |
| tile raw/legacy events | defensive accessor | bridge/TileStore normalization → `getEvent()` | ✓ WIRED | Tile tests verify real map flow, replacement, lookup, and isolation. |
## Data-Flow Trace (Level 4)
| Artifact | Data variable | Source | Produces real data | Status |
|---|---|---|---|---|
| `CoreState` | save/replay state | `MemorySaveSystem`, concrete hero/maps/enemy/flag state | Yes | ✓ FLOWING |
| serialized event adapter | event body/trigger | supplied `Statement[]` and `EventTrigger` load payload | Yes | ✓ FLOWING |
| raw map binding | coordinate event ids | `IMapRawData.events` consumed by `MapState.fromRaw()` | Yes | ✓ FLOWING |
| registered built-ins | map/hero/event mutations | AnonTokyo calls through `IBlockEventEnv` | Yes | ✓ FLOWING |
| replay commands | movement/item/equipment state | real CoreState APIs and awaited controllers | Yes | ✓ FLOWING |
| Node verifier | final snapshots | live `hero.attribute` and every map layer | Yes | ✓ FLOWING |
| TileStore | tile event maps | raw/legacy `events` input | Yes | ✓ FLOWING |
| type/circular gates | diagnostics/graph | real `vue-tsc` and Madge processes | Yes | ✓ FLOWING |
## Behavioral Spot-Checks
| Behavior | Command | Result | Status |
|---|---|---|---|
| Full Phase 3 data regression | `pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state` (sequential) | 19 files, 105 tests passed | ✓ PASS |
| Serialized event/map binding, all nullish built-ins, production replay safety | focused Vitest command for `coreSerializedEvents.test.ts`, `event.test.ts`, `commands.test.ts` | 3 files, 24 tests passed | ✓ PASS |
| Independent Node replay gate | `pnpm test:data-node` | `Node replay verifier passed`, exit 0 | ✓ PASS |
| Scoped type gate | `pnpm exec tsx script/check-data-type.ts` | 27 total, 0 in-scope, 27 outside-scope diagnostics; exit 0 | ✓ PASS |
| Scoped circular gate | `pnpm exec tsx script/check-data-circular.ts` | 0 total/in-scope/outside-scope cycles; exit 0 | ✓ PASS |
| Phase implementation lint | scoped `pnpm exec eslint ...` | 0 errors, 21 expected `no-console` warnings | ✓ PASS |
| Phase implementation formatting | scoped `pnpm exec prettier --check ...` | All matched files use Prettier code style | ✓ PASS |
The verifier initially launched the full workspace and scoped Vitest commands concurrently; that contention caused a `coreEventLayer.test.ts` `beforeAll` timeout. The authoritative sequential Phase 3 regression was then run alone and passed all 19 files/105 tests. No source changes were made between those runs; the timeout is treated as an execution-contention note, not a code failure.
## Probe Execution
No `scripts/*/tests/probe-*.sh` probe was declared or found for this phase. The documented executable process gate, `pnpm test:data-node`, was run independently and passed.
## Requirements Coverage
| Requirement | Source | Description | Status | Evidence |
|---|---|---|---|---|
| DATA-01 | All nine Phase 3 plans; `.planning/REQUIREMENTS.md` | Complete usable L0L3 data interfaces and independent Node replay verification | ✓ SATISFIED | All 26 truths verified; 19/105 data regression, Node process, type, circular, lint, and Prettier gates pass. |
No orphaned Phase 3 requirement was found. DATA-01 is the only requirement mapped to this phase and is declared by every Phase 3 plan.
## Test Quality Audit
| Test File | Active tests | Skipped | Circular expected-value generation | Assertion level | Verdict |
|---|---:|---:|---:|---|---|
| `data-common/src/store/tileStore.test.ts` | 5 | 0 | 0 | Value/behavioral | PASS |
| `data-common/src/store/eventStore.test.ts` | 3 | 0 | 0 | Value/behavioral | PASS |
| `data-common/src/common/mover.test.ts` | 4 | 0 | 0 | Behavioral | PASS |
| `data-base/src/map/mapLifecycle.test.ts` | 5 | 0 | 0 | Value/behavioral | PASS |
| `data-base/src/map/eventView.test.ts` | 2 | 0 | 0 | Value/behavioral | PASS |
| `data-base/src/map/eventPath.test.ts` | 9 | 0 | 0 | Behavioral | PASS |
| `data-system/src/event/eventDispatch.test.ts` | 6 | 0 | 0 | Behavioral/value | PASS |
| `data-system/src/path/system.test.ts` | 13 | 0 | 0 | Behavioral/value | PASS |
| `data-system/src/path/performance.test.ts` | 3 | 0 | 0 | Behavioral | PASS |
| `data-system/src/path/graph.test.ts` | 12 | 0 | 0 | Behavioral/value | PASS |
| `data-state/test/tileLegacy.test.ts` | 2 | 0 | 0 | Value/behavioral | PASS |
| `data-state/test/nodeTracer.test.ts` | 2 | 0 | 0 | Behavioral | PASS |
| `data-state/test/nodeReplay.test.ts` | 6 | 0 | 0 | Behavioral/value | PASS |
| `data-state/test/dataClosure.test.ts` | 6 | 0 | 0 | Value/behavioral | PASS |
| `data-state/test/coreSerializedEvents.test.ts` | 2 | 0 | 0 | Behavioral/value | PASS |
| `data-state/test/coreNode.test.ts` | 2 | 0 | 0 | Behavioral/value | PASS |
| `data-state/src/event/event.test.ts` | 10 | 0 | 0 | Behavioral/value | PASS |
| `data-state/src/replay/commands.test.ts` | 12 | 0 | 0 | Behavioral/value | PASS |
| `data-state/src/coreEventLayer.test.ts` | 1 | 0 | 0 | Value/behavioral | PASS |
**Total:** 105 active tests across 19 files; 0 disabled requirement-linked tests; 0 circular expected-value generators. Expected values are explicit fixtures/literals or independently constructed fakes, not generated by the system under test.
## Decision Coverage
The decision coverage gate ran against `03-CONTEXT.md`: **28/28 decisions honored**, with no non-honored decisions. This is non-blocking corroboration and does not replace the source/test evidence above.
## Anti-Patterns Found
| File | Pattern | Severity | Impact |
|---|---|---|---|
| Phase 3 changed implementation/test files | `TBD`/`FIXME`/`XXX` debt markers | ✓ NONE | No unreferenced debt-marker blocker. |
| `packages-user/data-common/src/save/memory.ts` | Intentional empty IndexedDB persistence branch | INFO | Node adapter must not touch IndexedDB; covered by the Node boundary and documented as an accepted adapter behavior. |
| `packages-user/data-state/src/legacy/dependencies.ts` | Intentional empty Node loading callback | INFO | Node path deliberately does not register browser loading; browser/legacy branch remains implemented. |
| `packages-user/data-state/src/ins.ts` | Pre-existing `TODO` about singleton weakening | INFO | File was not modified by the Phase 3 implementation/gap-closure commits; not a phase debt marker. |
No stub, orphaned artifact, hardcoded rendered data, or console-only implementation was found in the phase-owned gap-closure paths.
## Advisory (New Scope, Unevidenced)
None. Re-verification anti-pattern findings introduced no unevidenced blocker; no advisory item is carried forward.
## Human Verification Required
N/A — this is an infrastructure/data-layer phase with no user-facing UI or external-service acceptance step. All behavior-dependent phase truths have passing named tests, so `behavior_unverified` is 0.
## Gaps Summary
All three prior blockers are closed in the current codebase. Serialized events now enter through the internal CoreState load path and bind raw map event ids before replay; all eight built-in registration functions safely resolve for nullish parameters; and production replay commands use the approved safety decoration only at state-changing entrances. The sequential full data regression passes **19 files / 105 tests**, `pnpm test:data-node` passes, the scoped type gate reports **0 in-scope / 27 outside-scope**, the circular gate reports **0 cycles**, and scoped lint/Prettier checks pass. No blocking gaps remain.
---
_Verified: 2026-09-10T13:09:33Z_
_Verifier: the agent (gsd-verifier)_