diff --git a/CLAUDE.md b/CLAUDE.md index 8e146de..7e343da 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -618,16 +618,27 @@ acdream's plan lives in two files committed to the repo: approval. **Currently in Phase L.2 (Movement & Collision Conformance).** L.2a slices -1+2+3 + L.2d slice 1+1.5 shipped 2026-05-12. L.2d closed at the Holtburg -site ("watch-and-wait" — no more slices until a new shape-fidelity bug -shows up elsewhere); doorway blocker identified as a Door entity, not -building BSP. L.2g (dynamic PhysicsState toggling — doors) brainstormed -and design-spec'd 2026-05-12 evening; **the natural next step is the -L.2g slice 1 implementation** (parse `SetState` 0xF74B, plumb new -PhysicsState into ShadowObjectRegistry, verify Holtburg inn doorway). +1+2+3 + L.2d slice 1+1.5 + L.2g slice 1 shipped 2026-05-12. L.2g slice 1 +is CODE-COMPLETE (parser + registry mutator + WorldSession dispatcher + +GameWindow subscriber, 4 commits, build green, 6 new tests pass), but +its visual verification is **deferred to the B.4b session** — clicking +on a door does nothing today because Phase B.4's input-action handler +was never wired (the wire builders and bindings exist, but +`GameWindow.OnInputAction` has no case for `SelectDblLeft`, so the +outbound Use never sends). **The natural next step is Phase B.4b — +finish the outbound Use handler wiring** (subscribe `SelectDblLeft` → +`WorldPicker.Pick` → `InteractRequests.BuildUse` → send), then re-run +the Holtburg inn-doorway visual test which verifies both L.2g slice 1 +and B.4b in one pass. Estimated 30-50 LOC, ~30 min, 1-2 subagent +dispatches. + +L.2g slice 1 ship handoff: [`docs/research/2026-05-12-l2g-slice1-shipped-handoff.md`](docs/research/2026-05-12-l2g-slice1-shipped-handoff.md) +— full evidence + the 4 minor review notes + the 1 Important test-coverage +gap for `ShouldSkip` (the B.4b visual test's hex-dump will settle whether +ACE sends `state=0x4` alone or `0x14`). Design spec: [`docs/superpowers/specs/2026-05-12-l2g-dynamic-physicsstate-design.md`](docs/superpowers/specs/2026-05-12-l2g-dynamic-physicsstate-design.md). +Implementation plan: [`docs/superpowers/plans/2026-05-12-phase-l2g-slice1.md`](docs/superpowers/plans/2026-05-12-phase-l2g-slice1.md). L.2d ship handoff: [`docs/research/2026-05-13-l2d-slice1-shipped-handoff.md`](docs/research/2026-05-13-l2d-slice1-shipped-handoff.md). -L.2a→L.2d handoff (now superseded by the L.2d ship): [`docs/research/2026-05-12-l2a-shipped-l2d-handoff.md`](docs/research/2026-05-12-l2a-shipped-l2d-handoff.md). **Phase L.2a (Truth & Diagnostics) slices 1-3 shipped 2026-05-12.** Three commits land the L.2 "make every bad movement outcome explainable" @@ -705,18 +716,25 @@ together comprise the streaming + rendering perf foundation for the project. **Next phase candidates (in rough preference order):** -- **L.2g slice 1 implementation — dynamic PhysicsState toggling for doors.** - Direct continuation of tonight's L.2d slice 1.5 evidence: parse inbound - `SetState (0xF74B)` wire message, plumb the new `PhysicsState` value into - `ShadowObjectRegistry`'s cached entity state so the existing - `CollisionExemption.IsExempt(...)` check sees up-to-date bits, and verify - the Holtburg inn-door scenario walks through cleanly when the server flips - Ethereal. Unblocks the M1 demo's *"open the inn door"* line. Spec: - [`docs/superpowers/specs/2026-05-12-l2g-dynamic-physicsstate-design.md`](docs/superpowers/specs/2026-05-12-l2g-dynamic-physicsstate-design.md). - **Note:** triage the 8 pre-existing test failures still hanging over the - physics modules first (none introduced by L.2a/L.2d slices — verified by - stash + rerun — but most touch code adjacent to where L.2g will plumb the - new state mutator). +- **Phase B.4b — finish the outbound Use handler wiring.** + Direct M1 blocker discovered while running the L.2g slice 1 visual + test: the wire builders (`InteractRequests.BuildUse`), classes + (`SelectionState`, `WorldPicker`), input-action enum entries + (`SelectDblLeft` etc.), and keybindings (LMB-dblclick → `SelectDblLeft`) + all ship today, but `GameWindow.OnInputAction`'s switch has NO case + for any of the `Select*` actions — clicking on a door fires the + diagnostic `[input] SelectDblLeft Press` but nothing downstream + listens. Memory file `project_interaction_pipeline.md` updated to + reflect this reality. Shape: subscribe `InputAction.SelectDblLeft` + → build a world ray from current mouse → `WorldPicker.Pick(...)` → + store in `_selection` → call `InteractRequests.BuildUse(seq, guid)` + + `_liveSession.SendGameMessage(body)`. Probably also subscribe + `SelectLeft` for select-without-use and `UseSelected` for the R + hotkey. Estimate: 30-50 LOC, 1-2 subagent dispatches, ~30 min. + Verifies L.2g slice 1 in the same Holtburg-doorway visual test once + it lands. Full context: + [`docs/research/2026-05-12-l2g-slice1-shipped-handoff.md`](docs/research/2026-05-12-l2g-slice1-shipped-handoff.md) + "Why the visual test is deferred" section. - **Triage the chronic open-issue list** in `docs/ISSUES.md` — #2 (lightning), #4 (sky horizon-glow), #28 (aurora), #29 (cloud thinness), #37 (humanoid coat), #50 (stray tree), #41 (remote-motion blips) have been open since diff --git a/docs/ISSUES.md b/docs/ISSUES.md index cea28d6..19562fe 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -46,6 +46,65 @@ Copy this block when adding a new issue: # Active issues +## #57 — B.4 interaction-handler missing: clicking on doors / NPCs / items silently does nothing + +**Status:** OPEN +**Severity:** HIGH (M1 blocker — demo target *"open the inn door, click an NPC, pick up an item"* is fully blocked) +**Filed:** 2026-05-12 +**Component:** input / interaction / `GameWindow.OnInputAction` + +**Description:** Discovered 2026-05-12 while running the L.2g slice 1 +visual test. Phase B.4 (2026-04-28) shipped half of itself: the wire- +message builders (`InteractRequests.BuildUse` / `BuildUseWithTarget` / +`BuildPickUp`), the supporting classes (`SelectionState`, `WorldPicker`), +the `InputAction` enum entries (`SelectLeft`, `SelectDblLeft`, +`SelectRight`, `SelectDblRight`, `UseSelected`, `SelectionPickUp`, etc.), +and the default keybindings (LMB → `SelectLeft`, LMB-dblclick → +`SelectDblLeft`, RMB → `SelectRight`, R → `UseSelected`, F → +`SelectionPickUp`). What was never shipped: a case for ANY of those +actions in `GameWindow.OnInputAction`'s switch. The runtime diagnostic +`[input] SelectLeft Press` fires when you click — confirming the +dispatcher resolves the chord — but nothing downstream listens, so the +click silently does nothing. Neither does double-click, R, or F. The +inbound side (`MoveToObjectReceived`, `StateUpdated` after L.2g slice 1) +is wired and ready; the block is purely outbound. + +**Root cause / status:** B.4 handler integration step was evidently +dropped or never landed. Memory file +`memory/project_interaction_pipeline.md` was updated 2026-05-12 to +reflect this reality (previous text claimed shipped). + +**Files:** +- `src/AcDream.App/Rendering/GameWindow.cs` — `OnInputAction` switch + around line 8546+ has no `Select*` cases. +- `src/AcDream.Core.Net/Messages/InteractRequests.cs` — wire builders + exist but have zero callers in `src/`. +- `src/AcDream.Core/Selection/SelectionState.cs` — class exists, zero + production callers. +- `src/AcDream.App/Rendering/WorldPicker.cs` — class exists, zero + production callers. +- `src/AcDream.UI.Abstractions/Input/KeyBindings.cs:300-320` — bindings + for `SelectLeft` / `SelectDblLeft` / `SelectRight` / `SelectDblMid` + exist. + +**Research:** [docs/research/2026-05-12-l2g-slice1-shipped-handoff.md](research/2026-05-12-l2g-slice1-shipped-handoff.md) +"Why the visual test is deferred" section has the full investigation. + +**Acceptance:** Double-left-clicking on a door in the Holtburg inn +doorway sends a `0xF7B1 / 0x0036 Use` to the server, the server flips +the door's `Ethereal` bit and broadcasts `SetState (0xF74B)`, the +L.2g-slice-1 chain mutates `ShadowObjectRegistry`, the +`CollisionExemption.ShouldSkip` check honors it, and the player can +walk through the doorway. Visual verification + log grep (per the +L.2g handoff's reproducibility recipe) both pass. + +**Status promotion:** This is a phase-sized follow-up (estimated +30-50 LOC, ~30 min). Promoted to **Phase B.4b** in the L.2 milestone +context and the CLAUDE.md "Next phase candidates" list. Will be closed +as `DONE (promoted to Phase B.4b)` once that phase's design spec lands. + +--- + ## #55 — Static-entity slow path reports ~1.45M `meshMissing` per 5s at r4 standstill **Status:** OPEN diff --git a/docs/plans/2026-04-29-movement-collision-conformance.md b/docs/plans/2026-04-29-movement-collision-conformance.md index a4e6f29..05ab3b0 100644 --- a/docs/plans/2026-04-29-movement-collision-conformance.md +++ b/docs/plans/2026-04-29-movement-collision-conformance.md @@ -266,6 +266,24 @@ Full design spec: M1 critical path: this slice unblocks the *"open the inn door"* demo scenario. +Current shipped slice (2026-05-12): + +| Commit | Subject | +|---|---| +| `2459f28` | `feat(phys L.2g slice 1): inbound SetState (0xF74B) parser` | +| `d538915` | `feat(phys L.2g slice 1): ShadowObjectRegistry.UpdatePhysicsState` | +| `536a608` | `feat(phys L.2g slice 1): WorldSession dispatches SetState (0xF74B) + hex probe` | +| `108e386` | `feat(phys L.2g slice 1): GameWindow routes SetState + extends [entity-source] log` | + +Slice 1 is CODE-COMPLETE: parser + registry mutator + WorldSession +dispatcher + GameWindow subscriber. 6 new tests pass (3 parser + 3 +registry). Build clean. Per-commit + final integration code reviews +all approved. **Visual verification deferred to Phase B.4b** — the +inbound SetState chain can't fire at runtime until B.4b finishes the +outbound Use handler. See +[docs/research/2026-05-12-l2g-slice1-shipped-handoff.md](../research/2026-05-12-l2g-slice1-shipped-handoff.md) +for full evidence + the 4 minor + 1 Important review notes. + ## Named Retail Anchors Primary source: `docs/research/named-retail/acclient_2013_pseudo_c.txt`. diff --git a/docs/plans/2026-05-12-milestones.md b/docs/plans/2026-05-12-milestones.md index 4a1527f..94238cb 100644 --- a/docs/plans/2026-05-12-milestones.md +++ b/docs/plans/2026-05-12-milestones.md @@ -101,13 +101,19 @@ doorway. Open the inn door. Click an NPC and see selection feedback. Pick up an item from the ground. **Phases to ship:** -- **L.2 (all sub-lanes a–g)** — Movement & Collision Conformance. Currently - active; L.2a slices 1+2+3 + L.2d slice 1+1.5 shipped 2026-05-12. L.2g - (dynamic PhysicsState toggling — doors) brainstormed + design-spec'd - 2026-05-12 evening, implementation next. -- **B.4** — `Use` / `UseWithTarget` / `PickUp` outbound messages - (shipped 2026-04-28; remains in M1 scope until L.2g completes the - inbound-state half of the Use round-trip). +- **L.2 (all sub-lanes a–g)** — Movement & Collision Conformance. + L.2a slices 1+2+3 + L.2d slice 1+1.5 + L.2g slice 1 (code) shipped + 2026-05-12. L.2g slice 1 visual verification deferred to B.4b session + (the inbound SetState chain can't fire until the outbound Use sends). +- **B.4 / B.4b** — `Use` / `UseWithTarget` / `PickUp` interaction. + B.4 (2026-04-28) shipped the wire builders, `SelectionState`, + `WorldPicker`, the input-action enums, and the keybindings — but + *not* the `GameWindow.OnInputAction` handler that ties them + together. As of 2026-05-12, clicking on a door silently does nothing. + **B.4b** is the small follow-up slice (~30-50 LOC) that subscribes + `SelectDblLeft` and routes through pick → BuildUse → send. Once B.4b + lands, the same Holtburg-doorway visual test verifies both L.2g + slice 1 and B.4b in one pass. **Freeze on landing:** - L.2 zone (collision, cell ownership, transition parity, wire authority) diff --git a/docs/research/2026-05-12-l2g-slice1-shipped-handoff.md b/docs/research/2026-05-12-l2g-slice1-shipped-handoff.md new file mode 100644 index 0000000..8a682cd --- /dev/null +++ b/docs/research/2026-05-12-l2g-slice1-shipped-handoff.md @@ -0,0 +1,241 @@ +# L.2g slice 1 shipped — handoff (code-complete; visual test deferred) + +**Date:** 2026-05-12 evening. +**Branch:** `claude/gallant-mestorf-3bf2e3` (ready to merge to main). +**Predecessors:** +- [2026-05-13-l2d-slice1-shipped-handoff.md](2026-05-13-l2d-slice1-shipped-handoff.md) — the L.2d trace that identified Door entities as the Holtburg doorway blocker, motivating L.2g. +- [docs/superpowers/specs/2026-05-12-l2g-dynamic-physicsstate-design.md](../superpowers/specs/2026-05-12-l2g-dynamic-physicsstate-design.md) — the L.2g design spec (commit `2c10dd4`). +- [docs/superpowers/plans/2026-05-12-phase-l2g-slice1.md](../superpowers/plans/2026-05-12-phase-l2g-slice1.md) — the L.2g slice 1 implementation plan (commit `869677b`). + +--- + +## TL;DR + +L.2g slice 1 **code is complete and unit-tested.** The four commits land +the full inbound `SetState (0xF74B)` pipeline: parser → WorldSession +event → GameWindow handler → `ShadowObjectRegistry.UpdatePhysicsState`. +After this slice, the existing `CollisionExemption.ShouldSkip` +short-circuit (cited at `acclient_2013_pseudo_c.txt:276782`) honors +runtime ETHEREAL flips without any resolver-path edit. + +**The visual verification at Holtburg's inn doorway is deferred to the +next session.** Cause: Phase B.4's outbound Use handler turns out to be +unwired — clicking on a door silently does nothing because no +production code subscribes to the `SelectLeft` / `SelectDblLeft` input +actions. Without the outbound Use, the server never sees a "open the +door" request, so the inbound SetState we just ported never fires. + +L.2g slice 1 is the inbound half of the round-trip. Phase **B.4b** (a +small ~30-50 LOC slice) is the outbound half. Both halves are required +for the M1 demo target *"open the inn door."* B.4b is the next session's +work. + +--- + +## What shipped on this branch + +| Commit | Subject | +|---|---| +| [`2459f28`](.) | `feat(phys L.2g slice 1): inbound SetState (0xF74B) parser` | +| [`d538915`](.) | `feat(phys L.2g slice 1): ShadowObjectRegistry.UpdatePhysicsState` | +| [`536a608`](.) | `feat(phys L.2g slice 1): WorldSession dispatches SetState (0xF74B) + hex probe` | +| [`108e386`](.) | `feat(phys L.2g slice 1): GameWindow routes SetState + extends [entity-source] log` | + +Plus docs/scaffolding earlier in the session: +- `2c10dd4` — L.2g design spec + L.2 plan-of-record + milestones + CLAUDE.md updates. +- `869677b` — L.2g slice 1 implementation plan (this doc's companion). + +**Build:** clean. **Tests:** 6 new tests pass (3 for parser, 3 for +registry mutator). Full suite: 1037 pass / 8 pre-existing-baseline fail. +No regressions. Per-commit + final integration code reviews all approved. + +--- + +## What the code now does end-to-end + +When the server broadcasts a `SetState (0xF74B)`: + +1. **Parse** — `WorldSession`'s dispatcher routes opcode `0xF74B` into + `SetState.TryParse(body)`, which returns + `SetState.Parsed(Guid, PhysicsState, InstanceSequence, StateSequence)`. +2. **Probe** (gated on `ACDREAM_PROBE_BUILDING=1`) — one-shot per + session, dumps the first message's body bytes as + `[setstate-hex] body.len=N first-N-bytes: 4B F7 ...` for wire-format + confidence. +3. **Event** — `WorldSession.StateUpdated` fires with the parsed value. +4. **Subscribe** — `GameWindow.OnLiveStateUpdated` (added to the live- + session attach block alongside `OnLiveVectorUpdated`) calls + `_physicsEngine.ShadowObjects.UpdatePhysicsState(parsed.Guid, parsed.PhysicsState)`. +5. **Mutate** — `ShadowObjectRegistry.UpdatePhysicsState` walks every + per-cell list the entity occupies and rewrites `list[i] with { State = newState }`. +6. **Per-tick diagnostic** (same probe flag) — emits + `[setstate] guid=0x... state=0x... instSeq=... stateSeq=...` for the + greppable trail. +7. **Resolver** — next physics tick, `FindObjCollisions` calls + `CollisionExemption.ShouldSkip(entry.State, entry.Flags, moverState)` + on the entity. The check is unchanged from L.2d slice 1; it + short-circuits when `(state & ETHEREAL_PS) != 0 && (state & IGNORE_COLLISIONS_PS) != 0`. + +**Slice 0.5 freebie folded in:** all 6 `[entity-source]` probe-log +sites in `GameWindow.cs` now emit `state=0x{state:X8} flags={flags}` +so ETHEREAL flips are greppable end-to-end from spawn through state +change. + +--- + +## Why the visual test is deferred — the B.4 discovery + +Before launching the visual test, the user reported that right-click +in-client was bound to camera orbit (correctly), and asked whether +left-click should open a door. Investigation produced this finding: + +| Component | State | +|---|---| +| `InteractRequests.BuildUse(seq, guid)` wire builder | ✅ implemented + tested | +| `SelectionState`, `WorldPicker` classes | ✅ exist in source | +| `InputAction.SelectLeft` / `SelectDblLeft` / `SelectRight` enum | ✅ defined | +| KeyBindings: LMB → `SelectLeft`, LMB-dblclick → `SelectDblLeft`, RMB → `SelectRight` | ✅ wired in `KeyBindings.cs:300-320` | +| `GameWindow.OnInputAction` switch case for `Select*` | ❌ **missing** | +| Any production caller of `SelectionState`, `WorldPicker`, `InteractRequests.BuildUse` | ❌ **none in `src/`** | + +The diagnostic line `[input] SelectLeft Press` fires on LMB-click — the +dispatcher knows the action — but nothing downstream listens. The +click silently does nothing. The R hotkey similarly does nothing +because the corresponding `UseSelected` case is also absent from the +switch. + +So the M1 outbound Use path is **half-shipped**: every component below +the handler exists, but the handler that ties them together was never +landed (despite a 2026-04-28 memory entry claiming "B.4 shipped"). +Phase B.4b is the slice that fixes this. + +This is **not** an L.2g defect. L.2g's code path is correct and unit- +tested; it just can't be exercised at runtime until the outbound Use +sends a SetState-triggering request to the server. + +--- + +## Open notes from reviews (minor — defer to next polish pass) + +The per-commit and final integration code reviews approved every commit. +Four observations flagged as Minor that are worth folding into a future +polish pass: + +1. **`SetState.cs` "total body size" phrasing diverges from `VectorUpdate.cs`.** + New form: `"Total body size: 16 bytes (4-byte opcode + 12-byte payload)"`. + Sibling form: `"Total body size after opcode: 32 bytes"`. The new form + is more self-documenting, but the spec asked to align with the + sibling. Cosmetic. +2. **`[setstate-hex]` log uses redundant `Math.Min(body.Length, 32)`.** + Called twice in the same line; could be hoisted to a local. + Harmless for a one-shot diagnostic. +3. **`WorldSession.cs` uses the fully-qualified + `AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled`** instead + of adding `using AcDream.Core.Physics;` and using the short form. + Every other call site in `GameWindow.cs` and `BSPQuery.cs` uses the + unqualified form. Style inconsistency. +4. **`[setstate]` diagnostic emits guid + state as hex but instSeq + + stateSeq as decimal.** Cosmetic. + +### One Important review note (worth following up explicitly) + +The final integration reviewer flagged: the test +`UpdatePhysicsState_FlipsEthereal_NextLookupSeesNewBits` asserts the +cached state changes to `0x4` but does **not** verify the chain +through `CollisionExemption.ShouldSkip`. That short-circuit requires +**both** `ETHEREAL_PS (0x4)` AND `IGNORE_COLLISIONS_PS (0x10)` to be +set simultaneously (`(state & 0x4) && (state & 0x10)`). A state of +`0x4` alone does NOT exempt collision. Per the reviewer, ACE's +`PhysicsObj.cs:787-791` may set both bits when doors open (broadcast +value `0x14` or higher) — but this is not verified by the test suite. + +**The B.4b visual test will settle this definitively:** the slice-1 +hex-dump probe will capture the real `state=0x????????` wire value the +first time a door opens. If ACE sends `0x14` or higher, the existing +chain works as-is. If ACE sends `0x4` only, we need a tiny adjustment +to `CollisionExemption.cs` (the `&&` would become `||`, OR we make the +collision exemption fire on ETHEREAL alone, OR we widen the test). + +**Action for B.4b session:** after the door-open visual test, grep the +launch log for `[setstate-hex]` and the `[setstate]` line that fires on +the Use → confirm the state bits ACE actually sends. If `0x4` only, +file a tiny L.2g slice 1b to widen `CollisionExemption.ShouldSkip` or +the test's assertion. + +--- + +## Next session + +**Pick: Phase B.4b — finish the outbound Use handler wiring.** + +Concretely: +- Subscribe `InputAction.SelectDblLeft` in `GameWindow.OnInputAction` + switch. +- Build a world ray from current mouse position + (`WorldPicker.BuildRay(mouse, vp, view, proj)`). +- Pick the closest entity (`WorldPicker.Pick(ray, entities, cache, skipGuid, maxDist)`). +- Store result in `_selection` (`SelectionState.Set(guid)`). +- Call `InteractRequests.BuildUse(seq, guid)` + `_liveSession.SendGameMessage(body)`. +- Probably also subscribe `InputAction.SelectLeft` for select-without- + use (single-click selects; double-click selects + uses). +- Optionally subscribe `InputAction.UseSelected` (R hotkey) to send Use + on the already-selected guid. +- Sequence-number management — there's a game-action sequence counter + on `WorldSession` already used by the outbound chat path; reuse it. + +Estimate: 30-50 LOC, 1-2 subagent-driven implementations + reviews, ~30 min. + +Once B.4b lands, **immediately re-run the Holtburg inn doorway visual +test** with `ACDREAM_PROBE_BUILDING=1`. Both L.2g slice 1 + B.4b are +verified by the same scenario; no separate L.2g visual test needed. + +--- + +## Reproducibility + +Same launch recipe as L.2d slice 1 (see CLAUDE.md "Running the client +against the live server"). For visual test once B.4b lands: + +```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_PROBE_RESOLVE = "1" +dotnet run --project src\AcDream.App\AcDream.App.csproj -c Debug 2>&1 | + Tee-Object -FilePath "launch-l2g+b4b.log" +``` + +Then walk into the Holtburg inn doorway, double-left-click the door, +wait for the swing animation, walk through. After 30s, watch the +auto-close fire. + +After closing the client: + +```powershell +Select-String -Path launch-l2g+b4b.log -Pattern "setstate-hex|setstate.*guid|entity-source.*Door|input.*SelectDblLeft" +``` + +Expected matches: +- One `[setstate-hex] body.len=16 ...` line (confirms holtburger's 12-byte payload). +- One `[entity-source] name=Door ... state=0x00000000 flags=None ...` at spawn. +- An `[input] SelectDblLeft Press` when you double-click. +- A `[setstate] guid=0x000F... state=0x????????` after the door opens. +- A second `[setstate] guid=0x000F... state=0x00000000` ~30s later when auto-close fires. + +--- + +## Worktree state at handoff + +- Branch `claude/gallant-mestorf-3bf2e3` ready to merge to main. +- 6 commits ahead of main: `2c10dd4` (spec + docs), `869677b` (plan), + `2459f28` / `d538915` / `536a608` / `108e386` (L.2g slice 1 code). +- One launch.log artifact (`launch-l2g-slice1.log`) in the working + tree from the attempted visual test — **not committed** (gitignored + or transient). Safe to discard; B.4b will produce a fresh log. + +User wants to start a fresh session for B.4b.