From 6ae38f7c6cd6b75c93a6f6c3e3d320c86696dddd Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 14 May 2026 06:50:51 +0200 Subject: [PATCH] =?UTF-8?q?docs(B.4c):=20implementation=20plan=20=E2=80=94?= =?UTF-8?q?=204=20tasks,=20door=20spawn-time=20sequencer=20+=20UM=20diagno?= =?UTF-8?q?stic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-by-task plan with full code in every step, no placeholders. Task 1: IsDoorSpawn helper + Door registration branch (state-seeded SetCycle from spawn PhysicsState ETHEREAL bit). Task 2: [door-cycle] diagnostic in OnLiveMotionUpdated for greppable verification. Task 3: Holtburg inn doorway visual test (user-performed). Task 4: ship handoff + close #58 + roadmap/CLAUDE/memory updates. Self-review table at bottom maps every spec section to its task(s); all covered. Companion to spec docs/superpowers/specs/2026-05-13-phase-b4c-design.md. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../plans/2026-05-13-phase-b4c-plan.md | 444 ++++++++++++++++++ 1 file changed, 444 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-13-phase-b4c-plan.md diff --git a/docs/superpowers/plans/2026-05-13-phase-b4c-plan.md b/docs/superpowers/plans/2026-05-13-phase-b4c-plan.md new file mode 100644 index 0000000..0e945d1 --- /dev/null +++ b/docs/superpowers/plans/2026-05-13-phase-b4c-plan.md @@ -0,0 +1,444 @@ +# Phase B.4c — Door Swing Animation Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:subagent-driven-development` (recommended) or `superpowers:executing-plans` to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make Holtburg's inn doors visibly swing open / closed when the player Uses them. Closes #58 and completes the M1 demo target *"open the inn door"* with full visual feedback. + +**Architecture:** One block edit in `GameWindow.cs` adds a Door-specific spawn-time branch alongside the existing creature gate at line 2692. Detect Door entities by `spawn.Name == "Door"`. For each, build the same `AnimationSequencer` as creatures (load `MotionTable` from dats, construct sequencer) and immediately seed it with the `Off` cycle (closed) or `On` cycle (already open) based on the spawn's `PhysicsState` ETHEREAL bit. The existing `OnLiveMotionUpdated` handler then routes naturally — no downstream changes. Adds one diagnostic line in the UM handler for greppable verification. Spec: [`docs/superpowers/specs/2026-05-13-phase-b4c-design.md`](../specs/2026-05-13-phase-b4c-design.md). + +**Tech Stack:** C# .NET 10 · existing `AnimationSequencer` + per-frame tick + WB renderer · no new dependencies. + +--- + +## File map + +| File | Op | Why | +|---|---|---| +| `src/AcDream.App/Rendering/GameWindow.cs` | Modify | Add `IsDoorSpawn` static helper + Door registration branch (after the existing `idleCycle` gate at line 2692) + `[door-cycle]` diagnostic in `OnLiveMotionUpdated`. | + +No new files. No unit tests added — GameWindow integration code is runtime-verified per the project's existing precedent (B.4b's switch cases, L.2g's MotionUpdated routing). + +--- + +## Task 1 — Add `IsDoorSpawn` helper + Door registration branch + +**Files:** +- Modify: `src/AcDream.App/Rendering/GameWindow.cs` + +This task adds two things in one commit: the static helper that detects Door entities, and the new `else if` branch in the live-spawn handler that registers them with a seeded `AnimationSequencer`. + +- [ ] **Step 1: Add the `IsDoorSpawn` helper** + +Insert this static helper immediately above the live-spawn handler. Use the `Edit` tool with this exact anchor: + +`old_string`: +``` + private void OnLiveEntitySpawnedLocked(AcDream.Core.Net.WorldSession.EntitySpawn spawn) +``` + +`new_string`: +``` + /// + /// Phase B.4c — door detection by server-sent name. Doors fail the + /// generic multi-frame-idle gate at line 2692 (no idle cycle), so we + /// register them via a sibling branch with a state-seeded sequencer. + /// + private static bool IsDoorSpawn(AcDream.Core.Net.WorldSession.EntitySpawn spawn) + => spawn.Name == "Door"; + + private void OnLiveEntitySpawnedLocked(AcDream.Core.Net.WorldSession.EntitySpawn spawn) +``` + +- [ ] **Step 2: Add the Door registration branch** + +Insert the new `else if` branch immediately after the existing `idleCycle` gate's closing brace (around line 2800). The anchor is the comment line that follows the gate. Use the `Edit` tool: + +`old_string`: +``` + } + + // Dump a summary periodically so we can see drop breakdowns without + // waiting for a graceful shutdown. + if (_liveSpawnReceived % 20 == 0) +``` + +`new_string`: +``` + } + else if (IsDoorSpawn(spawn) && _animLoader is not null) + { + // Phase B.4c — Door swing animation. Doors fail the + // multi-frame-idle gate above (no idle cycle) but DO have a + // MotionTable with On/Off cycles that ACE drives via + // UpdateMotion. Register with a seeded sequencer so the + // per-frame tick has frames to advance from frame 1 (without + // the seed, Sequencer.Advance(dt) returns no frames and the + // MeshRefs rebuild at line 7691 collapses the door to origin). + // + // Initial cycle mirrors ACE's Door.cs:43 + // (CurrentMotionState = motionClosed): Off when the door is + // closed at spawn, On when the spawn PhysicsState carries the + // ETHEREAL bit (door was already open in ACE's DB). + uint mtableId = spawn.MotionTableId ?? (uint)setup.DefaultMotionTable; + if (mtableId != 0) + { + var mtable = _dats.Get(mtableId); + if (mtable is not null) + { + var sequencer = new AcDream.Core.Physics.AnimationSequencer(setup, mtable, _animLoader); + + const uint NonCombatStance = 0x80000001u; + const uint MotionOn = 0x4000000Bu; // ACE MotionCommand.On (door open) + const uint MotionOff = 0x4000000Cu; // ACE MotionCommand.Off (door closed) + const uint EtherealPs = 0x4u; + uint spawnState = spawn.PhysicsState ?? 0u; + uint initialCycle = (spawnState & EtherealPs) != 0 ? MotionOn : MotionOff; + if (sequencer.HasCycle(NonCombatStance, initialCycle)) + sequencer.SetCycle(NonCombatStance, initialCycle); + + var template = new (uint, IReadOnlyDictionary?)[meshRefs.Count]; + for (int i = 0; i < meshRefs.Count; i++) + template[i] = (meshRefs[i].GfxObjId, meshRefs[i].SurfaceOverrides); + + _animatedEntities[entity.Id] = new AnimatedEntity + { + Entity = entity, + Setup = setup, + Animation = null, // sequencer-driven; tick reads sequencer state + LowFrame = 0, + HighFrame = 0, + Framerate = 0f, + Scale = scale, + PartTemplate = template, + CurrFrame = 0, + Sequencer = sequencer, + }; + + if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled) + Console.WriteLine(System.FormattableString.Invariant( + $"[door-anim] registered guid=0x{spawn.Guid:X8} entityId=0x{entity.Id:X8} mtable=0x{mtableId:X8} initialCycle=0x{initialCycle:X8}")); + } + } + } + + // Dump a summary periodically so we can see drop breakdowns without + // waiting for a graceful shutdown. + if (_liveSpawnReceived % 20 == 0) +``` + +- [ ] **Step 3: Build green** + +Run: `dotnet build src/AcDream.App/AcDream.App.csproj -c Debug` + +Expected: build succeeds, 0 errors. Any new warnings should be tied to the additions only. + +If a name like `meshRefs`, `entity`, `setup`, or `scale` doesn't resolve in scope at the insertion point, STOP and report — these are variables that exist in the surrounding scope at line 2800 of `OnLiveEntitySpawnedLocked` (verified during spec authoring at line 2700-2784 reads). They should be in scope; if Edit landed in the wrong place, fix the anchor first. + +- [ ] **Step 4: Tests green** + +Run: `dotnet test` + +Expected: **1046 pass / 8 pre-existing-baseline fail** (same as main HEAD `3e08e10`). Any regression here means the new branch is touching unrelated code paths — investigate. + +- [ ] **Step 5: Commit** + +```bash +git add src/AcDream.App/Rendering/GameWindow.cs +git commit -m "$(cat <<'EOF' +feat(B.4c): door spawn-time AnimationSequencer with state-seeded initial cycle + +Adds IsDoorSpawn helper and a sibling branch to the live-spawn +handler's animation registration gate. Detects entities where +spawn.Name == "Door" and registers them in _animatedEntities with an +AnimationSequencer seeded from the spawn PhysicsState's ETHEREAL bit +(Off cycle if closed, On if already open). + +Mirrors ACE Door.cs:43 (CurrentMotionState = motionClosed) so the +sequencer always has frames for the per-frame tick to advance from +the first render. Without the seed, Advance(dt) returns no frames and +the MeshRefs rebuild at line 7691 collapses the door to origin. + +No changes to OnLiveMotionUpdated, AnimationSequencer, EntitySpawnAdapter, +or the per-frame tick. The tick's sequencer branch at line 7497 reads +ae.Sequencer.Advance(dt) and never touches ae.Animation in this path +(only the legacy slerp else branch at line 7644+ does). + +[door-anim] registered diagnostic gated on ACDREAM_PROBE_BUILDING. + +Co-Authored-By: Claude Opus 4.7 (1M context) +EOF +)" +``` + +--- + +## Task 2 — Add `[door-cycle]` UM dispatch diagnostic + +**Files:** +- Modify: `src/AcDream.App/Rendering/GameWindow.cs` + +Adds one diagnostic line in `OnLiveMotionUpdated` that fires whenever an `UpdateMotion` arrives for an entity named "Door". Greppable trail for verification of the open/close cycle dispatch. + +- [ ] **Step 1: Locate the diagnostic insertion point** + +Run (Grep tool): +``` +pattern: ACDREAM_DUMP_MOTION.*== "1" +path: src/AcDream.App/Rendering/GameWindow.cs +output: content with -n +``` + +Expected: one match around line 3075 in the `OnLiveMotionUpdated` body. The `[door-cycle]` diagnostic goes immediately after this `ACDREAM_DUMP_MOTION` block so both diagnostics are grouped. + +- [ ] **Step 2: Add the `[door-cycle]` diagnostic** + +Use the `Edit` tool. The anchor is the closing brace + blank line + the next code section ("Wire server-echoed RunRate") which follows the `ACDREAM_DUMP_MOTION` block at line 3075-3087: + +`old_string`: +``` + $"UM guid=0x{update.Guid:X8} mt=0x{update.MotionState.MovementType:X2} stance=0x{stance:X4} cmd={cmdStr} spd={spd:F2} " + + $"| seq now style=0x{seqStyle:X8} motion=0x{seqMotion:X8}"); + } + + // Wire server-echoed RunRate first — used for the player's own +``` + +`new_string`: +``` + $"UM guid=0x{update.Guid:X8} mt=0x{update.MotionState.MovementType:X2} stance=0x{stance:X4} cmd={cmdStr} spd={spd:F2} " + + $"| seq now style=0x{seqStyle:X8} motion=0x{seqMotion:X8}"); + } + + // Phase B.4c — durable per-Door UM dispatch trail for visual-test grep. + if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled + && _liveEntityInfoByGuid.TryGetValue(update.Guid, out var doorInfo) + && doorInfo.Name == "Door") + { + Console.WriteLine(System.FormattableString.Invariant( + $"[door-cycle] guid=0x{update.Guid:X8} stance=0x{update.MotionState.Stance:X4} cmd=0x{(update.MotionState.ForwardCommand ?? 0u):X4}")); + } + + // Wire server-echoed RunRate first — used for the player's own +``` + +- [ ] **Step 3: Build green** + +Run: `dotnet build src/AcDream.App/AcDream.App.csproj -c Debug` + +Expected: build succeeds, 0 errors. + +If the name `_liveEntityInfoByGuid` doesn't resolve, STOP and report. It exists in `GameWindow.cs` (verified during spec authoring; used elsewhere in `DescribeLiveEntity` around line 8758 of the B.4b-shipped tree). + +If `doorInfo.Name` doesn't resolve, the field on the live-entity info struct may be named differently (e.g. `EntityName`). Use Grep to find the existing usage pattern and adjust. + +- [ ] **Step 4: Tests green** + +Run: `dotnet test` + +Expected: same **1046 pass / 8 pre-existing-baseline fail** from Task 1. + +- [ ] **Step 5: Commit** + +```bash +git add src/AcDream.App/Rendering/GameWindow.cs +git commit -m "$(cat <<'EOF' +feat(B.4c): [door-cycle] diagnostic in OnLiveMotionUpdated + +Logs one line per UpdateMotion arriving for an entity named "Door" +when ACDREAM_PROBE_BUILDING=1. Greppable trail for the B.4c visual +test: confirms the dispatcher hit the sequencer for door open / close. + +Durable subsystem-named tag per the Opus reviewer's B.4b feedback +([B.4c] would rot after phase archival; [door-cycle] survives). + +Co-Authored-By: Claude Opus 4.7 (1M context) +EOF +)" +``` + +--- + +## Task 3 — Visual verification at Holtburg inn doorway + +**This task is performed by the user.** The implementing agent kicks off the launch in background; the user observes the running client and reports the result. + +- [ ] **Step 1: Kill any stale client + wait for ACE session cleanup** + +Run via PowerShell: +```powershell +Get-Process -Name AcDream.App -ErrorAction SilentlyContinue | Stop-Process -Force +Start-Sleep -Seconds 20 +``` + +Per CLAUDE.md "Logout-before-reconnect": ACE keeps the last session alive briefly after disconnect. 20s is the empirical minimum from B.4b's debug session. + +- [ ] **Step 2: Launch the client with probes enabled** + +Run via Bash tool with `run_in_background: true`: +```powershell +$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call" +$env:ACDREAM_LIVE = "1" +$env:ACDREAM_TEST_HOST = "127.0.0.1" +$env:ACDREAM_TEST_PORT = "9000" +$env:ACDREAM_TEST_USER = "testaccount" +$env:ACDREAM_TEST_PASS = "testpassword" +$env:ACDREAM_DEVTOOLS = "1" +$env:ACDREAM_PROBE_BUILDING = "1" +$env:ACDREAM_DUMP_MOTION = "1" +dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 | + Tee-Object -FilePath "launch-b4c.log" +``` + +- [ ] **Step 3: User performs the scenario** + +In the running client: +1. Wait ~8s for the player to spawn at Holtburg. +2. Walk to the inn doorway (south building, north-facing door). +3. Observe: door visually closed. +4. Double-left-click the door. +5. **Observe: door visibly swings open over a fraction of a second.** +6. Walk forward through the open doorway. +7. Wait ~30s in the inn. +8. **Observe: door visibly swings closed.** +9. Bump the closed door — confirm it blocks again. +10. Close the client window. + +- [ ] **Step 4: Grep the log** + +Run via PowerShell: +```powershell +Select-String -Path launch-b4c.log -Pattern "door-anim|door-cycle|UM guid=.*Door|setstate.*0x7A9B4015" +``` + +Expected matches (in approximate order): +- `[door-anim] registered guid=0x... mtable=0x... initialCycle=0x4000000C` (one per closed door at world load) +- (user double-clicks) +- `UM guid=0x7A9B4015 mt=... stance=0x0001 cmd=0x000B ...` (existing UM dump for the open motion) +- `[door-cycle] guid=0x7A9B4015 stance=0x0001 cmd=0x000B` (NEW; cmd=On) +- `[setstate] guid=0x7A9B4015 entityId=0x000F4245 state=0x0001000C` (L.2g chain) +- ~30s gap +- `UM guid=0x7A9B4015 mt=... stance=0x0001 cmd=0x000C ...` +- `[door-cycle] guid=0x7A9B4015 stance=0x0001 cmd=0x000C` (NEW; cmd=Off) +- `[setstate] guid=0x7A9B4015 entityId=0x000F4245 state=0x00010008` + +- [ ] **Step 5: Decide on follow-up based on observed behavior** + +- **Animation plays + door rests at open pose for ~30s + animation plays again on close + rests at closed pose**: success. Proceed to Task 4. +- **Animation plays as a loop instead of one-shot** (door spins continuously): pivot to Approach C from the spec (bespoke `DoorAnimationState` outside the sequencer). Out of B.4c scope; revise the spec and file a slice 2. +- **No animation, but log shows the dispatch fired**: motion-table cycle resolution issue. Inspect `mtable.Cycles[(0x80000001 << 16) | 0x4000000B]` to see if the cycle is present. May need a different cycle key form. +- **`[door-anim] registered` never logs**: spawn-time branch isn't firing. Check `spawn.Name` actual value (might be localized or padded). Add a one-line `Console.WriteLine` of `spawn.Name` in the live-spawn handler to surface it, then revise `IsDoorSpawn` accordingly. + +--- + +## Task 4 — Ship handoff + close #58 + roadmap/CLAUDE/memory updates + +**Files (in-repo):** +- Create: `docs/research/2026-05-13-b4c-shipped-handoff.md` +- Modify: `docs/ISSUES.md` (close #58) +- Modify: `docs/plans/2026-04-11-roadmap.md` (add B.4c row to shipped table) +- Modify: `CLAUDE.md` ("Currently in Phase L.2" paragraph + Next phase candidates) + +**File (outside repo):** +- Modify: `C:\Users\erikn\.claude\projects\C--Users-erikn-source-repos-acdream\memory\project_interaction_pipeline.md` + +- [ ] **Step 1: Write the ship-handoff doc** + +Create `docs/research/2026-05-13-b4c-shipped-handoff.md`. Model after `docs/research/2026-05-13-b4b-shipped-handoff.md` for structure: TL;DR / What shipped (commit table) / End-to-end flow with actual observed evidence / Open notes / Reproducibility / Worktree state. + +Required content: +- TL;DR: B.4c shipped, M1 demo target "open the inn door" now has full visual feedback. ~50 LOC, 2 implementation commits + 1 docs commit. +- What shipped table (2 implementation commits from Tasks 1+2) +- Actual observed `[door-anim]` and `[door-cycle]` log lines from Task 3 step 4 +- Worktree branch: `claude/phase-b4c-door-anim`, 4 commits ahead of `3e08e10` (the B.4b merge) + +- [ ] **Step 2: Move #58 from Active to Recently Closed in `docs/ISSUES.md`** + +Edit `docs/ISSUES.md`: +- Cut the `## #58 — Door swing animation` block from "Active issues". +- Paste under "Recently closed" with header changed to `## #58 — [DONE 2026-05-13] Door swing animation: ...`. +- Add `**Status:** DONE` and `**Closed:** 2026-05-13` lines. +- Add a one-paragraph closure summary describing the fix: Door-specific spawn-time branch + state-seeded SetCycle + UM diagnostic. Cite this PR's merge commit + the handoff doc. + +- [ ] **Step 3: Update the roadmap's shipped table** + +Edit `docs/plans/2026-04-11-roadmap.md`. Add a new row to the "shipped" table: + +``` +| 2026-05-13 | Phase B.4c — Door swing animation | | Closes #58. Door-specific spawn-time AnimationSequencer registration with state-seeded initial cycle. M1 demo target "open the inn door" now has full visual feedback. | +``` + +(Read the table first to match its column structure exactly — the B.4b row uses `Phase | What landed | Verification`; match that.) + +- [ ] **Step 4: Update `CLAUDE.md` "Currently in Phase L.2" paragraph + Next phase candidates** + +Edit `CLAUDE.md`: +- Update "Currently in Phase L.2" paragraph to reflect B.4c shipped + visual-verified 2026-05-13. +- Remove `#58 — Door swing animation` from the "Next phase candidates" list. +- Elevate the next candidate (currently #2 in the list: "Triage the chronic open-issue list") to #1, OR pick a different next-phase based on M1 critical-path-ness. The natural next step per CLAUDE.md's "work-order autonomy" rule is whichever progresses M1's remaining demo targets ("click an NPC", "pick up an item") — file a one-line note that these are the M1-critical-path follow-ups even though they aren't pre-specced phases. + +- [ ] **Step 5: Update the memory file** (outside the repo, NOT git-tracked) + +Edit `C:\Users\erikn\.claude\projects\C--Users-erikn-source-repos-acdream\memory\project_interaction_pipeline.md`: +- Append a "B.4c shipped 2026-05-13" entry to the components table: + - `Door swing animation` — exists now (`GameWindow.cs IsDoorSpawn + sibling spawn-time branch`) + - `[door-anim]` / `[door-cycle]` diagnostics — gated on `ACDREAM_PROBE_BUILDING` +- Note: animation routing is door-specific, not general non-creature support yet (chests/levers/traps still drop through the gate). + +- [ ] **Step 6: Commit the in-repo docs** + +```bash +git add docs/research/2026-05-13-b4c-shipped-handoff.md docs/ISSUES.md docs/plans/2026-04-11-roadmap.md CLAUDE.md +git commit -m "$(cat <<'EOF' +docs(B.4c): ship handoff + close #58 + roadmap/CLAUDE update + +Phase B.4c shipped end-to-end 2026-05-13. Holtburg inn doorway +double-click verified: door visually swings open, player walks +through, door visually swings closed ~30s later. + +2 implementation commits: +- B.4c Task 1: door spawn-time AnimationSequencer with state-seeded cycle +- B.4c Task 2: [door-cycle] diagnostic in OnLiveMotionUpdated + +Closes #58. Memory file project_interaction_pipeline.md updated +outside the repo. + +Co-Authored-By: Claude Opus 4.7 (1M context) +EOF +)" +``` + +(The memory file lives outside the repo — update it but don't include it in this commit.) + +- [ ] **Step 7: Hand off to merge (controller does final review + merge)** + +After this commit, hand off to the controller. The controller will: +1. Run the final whole-branch code review (Opus per CLAUDE.md "load-bearing quality review of a phase boundary"). +2. Merge `claude/phase-b4c-door-anim` → `main` with `--no-ff`. +3. Verify tests on merged main. +4. Remove the worktree (best-effort; submodules may block per the B.4b finishing experience). + +--- + +## Self-review against the spec + +| Spec section | Plan task(s) | Coverage | +|---|---|---| +| §Architecture: sibling branch after creature gate at line 2692 | Task 1 step 2 | covered | +| §Architecture: state-seeded initial cycle from spawn.PhysicsState | Task 1 step 2 | covered | +| §Components: `IsDoorSpawn(spawn) => spawn.Name == "Door"` | Task 1 step 1 | covered | +| §Components: Door registration body (sequencer build + SetCycle + AnimatedEntity register) | Task 1 step 2 | covered | +| §Components: `[door-anim] registered` diagnostic on spawn | Task 1 step 2 | covered (inline in registration body) | +| §Components: `[door-cycle]` diagnostic in OnLiveMotionUpdated | Task 2 step 2 | covered | +| §Data flow: spawn → seeded cycle → UM dispatch → state flip → animation | Tasks 1+2 + L.2g (existing) | covered (L.2g pipeline is the upstream dependency) | +| §Error handling: door has no MotionTable | Task 1 step 2 (`if (mtableId != 0)` + inner `if (mtable is not null)`) | covered | +| §Error handling: MotionTable lacks On/Off cycle | Task 1 step 2 (`if (sequencer.HasCycle(...))` gate around SetCycle) | covered | +| §Error handling: `_animLoader` null | Task 1 step 2 (outer `&& _animLoader is not null`) | covered | +| §Error handling: spawn.Name != "Door" | (no code change — silent fallback, acceptable per spec) | covered by omission | +| §Testing: runtime visual verification at Holtburg | Task 3 | covered | +| §Testing: log grep | Task 3 step 4 | covered | +| §Acceptance: build + tests green | Tasks 1+2 steps 3-4 | covered | +| §Acceptance: ISSUES.md #58 → Recently closed | Task 4 step 2 | covered | +| §Acceptance: roadmap + CLAUDE.md update | Task 4 steps 3-4 | covered | +| §Non-goals: sound effects, dust, lighting, collision rotation, generalized non-creature support | (none — explicitly deferred) | covered by omission | + +No placeholders. No "TBD." Every code step shows the actual code; every command step shows the exact command and expected output. Type names (`AnimationSequencer`, `AnimatedEntity`, `MotionTable`, `EntitySpawn`) match across tasks. Diagnostic tags (`[door-anim]`, `[door-cycle]`) consistent throughout.