docs(phys L.2g): slice 1 shipped handoff + B.4 gap discovery + plan-of-record

L.2g slice 1 is CODE-COMPLETE: parser + registry mutator + WorldSession
dispatcher + GameWindow subscriber (4 commits: 2459f28, d538915,
536a608, 108e386). Build clean, 6 new tests pass, baseline-stable
across the full suite. Per-commit + final integration code reviews
all approved.

Visual verification deferred: while running the Holtburg-doorway test,
Phase B.4's outbound Use handler turned out to be unwired. The wire
builders (InteractRequests.BuildUse), classes (SelectionState,
WorldPicker), input-action enums, and keybindings all exist — but
GameWindow.OnInputAction has no case for SelectDblLeft, so the click
silently does nothing. The inbound L.2g chain we just landed can't
fire until something sends an outbound Use.

This commit captures the handoff + reframes next-session work:

  * docs/research/2026-05-12-l2g-slice1-shipped-handoff.md (NEW)
    Full evidence: 4 shipped commits, end-to-end code flow, B.4
    discovery explanation, 4 minor + 1 Important review notes
    (the Important one is a test-coverage gap that the B.4b visual
    test will settle automatically), reproducibility recipe,
    next-session pick.

  * CLAUDE.md
    "Currently in Phase L.2" paragraph: L.2g slice 1 code shipped;
    visual test deferred to B.4b. Next-phase-candidates list:
    L.2g slice 1 (now done) replaced with the B.4b candidate
    pointing at the slice scope.

  * docs/plans/2026-04-29-movement-collision-conformance.md
    L.2g section gains a "Current shipped slice (2026-05-12):" table
    listing the 4 commits.

  * docs/plans/2026-05-12-milestones.md
    M1 phase-list updated: L.2g slice 1 (code) shipped; B.4 renamed
    "B.4 / B.4b" with the gap-discovery note + B.4b shape.

  * docs/ISSUES.md
    New issue #57 (HIGH) for the B.4 interaction-handler gap.
    Promoted to Phase B.4b; will close as
    DONE (promoted to Phase B.4b) when B.4b's design spec lands.

  * Memory file project_interaction_pipeline.md (in personal
    memory dir, not in this commit) updated to reflect reality.

Next session: Phase B.4b (~30-50 LOC, 1-2 subagent dispatches,
~30 min). Subscribe SelectDblLeft -> WorldPicker.Pick ->
InteractRequests.BuildUse -> _liveSession.SendGameMessage. Same
Holtburg-doorway visual test verifies both L.2g slice 1 and B.4b
in one pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-12 23:17:05 +02:00
parent 108e3868a5
commit aba6c9ac7f
5 changed files with 369 additions and 27 deletions

View file

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

View file

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

View file

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

View file

@ -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 ag)** — 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 ag)** — 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)

View file

@ -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.