Foundation work (6 commits ff548b9..f845b22) landed but visual
verification 2026-05-19 FAILED to fix the user-reported indoor bugs.
Documenting the deeper diagnosis + the next phase target without
reverting the foundation work.
What landed (kept):
- BSPQuery.FindWalkableInternal gained ref ushort hitPolyId (Task 1).
- New public BSPQuery.FindWalkableSphere wrapper over the existing
retail-faithful walkable finder (Task 2).
- Transition.TryFindIndoorWalkablePlane refactored through it,
PointInPolygonXY deleted (Task 3).
- [indoor-walkable] runtime-toggleable probe (Task 4).
- 5 new tests + 9 updated existing tests, all green; build clean.
What didn't fix: cellar descent FAIL, 2nd-floor walking FAIL
(intermittent falling-stuck), single-floor cottage REGRESSION (was
stable, now intermittent falling-stuck), phantom collisions PERSIST.
Probe evidence: 1443 MISS / 2 HIT over 1445 calls. Smoking gun:
foot-sphere-tangent-to-floor case fails PolygonHitsSpherePrecise's
|dist| > radius - epsilon check by ~0.0002. The BSP walker is
correct; the caller (TryFindIndoorWalkablePlane) is misusing it.
Root cause (deeper than originally diagnosed): TryFindIndoorWalkablePlane
exists only as a Phase 2 commit eb0f772 stop-gap. Retail doesn't
synthesize a ContactPlane per frame — retail RETAINS the previous
frame's plane when the BSP says no collision. Retail's find_walkable
only runs inside step_sphere_down (a sweep), never as a standing-still
query.
Next phase target: port retail's ContactPlane retention so the
resolver retains state across frames. Likely eliminates the per-frame
TryFindIndoorWalkablePlane call entirely. Foundation work (BSP walker
+ probe + tests) remains useful regardless.
ISSUES #83 remains OPEN with the deeper diagnosis.
Roadmap header updated to reflect partial-ship status.
Handoff at docs/research/2026-05-19-indoor-walkable-plane-bsp-port-shipped-handoff.md.
Spec: docs/superpowers/specs/2026-05-19-indoor-walkable-plane-bsp-port-design.md
Plan: docs/superpowers/plans/2026-05-19-indoor-walkable-plane-bsp-port.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes ISSUES.md #87 + #85 + the remaining wall-pass-through portion of
#84 (fully closes#84). Portal-graph cell traversal replaces Phase D's
AABB containment. Walking through doors promotes/demotes CellId correctly
via portal traversal; walls block from inside indoor cells; indoor walkable
plane is synthesized from the cell's floor poly so the resolver tracks
walkability correctly during indoor movement.
Files two new issues: #88 (indoor static objects vibrate — pre-existing,
spotted during Phase 2 testing) and #89 (BSPQuery.SphereIntersectsCellBsp
— follow-up to make CheckBuildingTransit retail-faithful; currently uses
radius-less PointInsideCellBsp as a documented approximation).
ISSUES.md: #87, #85, #84 moved to DONE. #88 + #89 filed.
Roadmap: Indoor walking Phase 2 added to shipped table.
CLAUDE.md: recent-phase paragraph updated to reflect Phase 2 shipped.
New handoff: docs/research/2026-05-19-indoor-walking-phase2-shipped-handoff.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cluster A's investigation pinned #86 (picker) as structural and closed
it (Phase B). #84 and #85 both pinned on missing indoor cell tracking;
Phase D promoted CellId via AABB containment which un-stuck the
spawn-in-building case (closes#84 partially) but proved too tight for
threshold/doorway cells to keep CellId indoor during normal walking.
The proper fix is retail's portal-based cell traversal; filed as a
new ISSUES.md issue (see body) for the follow-up phase. Phase E
diagnostic infrastructure ([cell-cache] + extended [indoor-bsp]) stays
in place as scaffolding for that work.
ISSUES.md: #86 → Recently closed. #84 status updated to PARTIAL with
resolution paragraph. #85 status update note added. New issue #87 filed
for portal-based indoor cell tracking.
Roadmap: Cluster A added to Recently shipped with partial-ship note.
Forward entry added for the portal-traversal follow-up under Phase G.
CLAUDE.md: current-phase paragraph updated to reflect Cluster A partial
ship. Next phase deferred to Claude's choice in a future session.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2's one-line WB patch (Setup-prefix guard at ObjectMeshManager.cs:1230)
fixed the symptom but is structurally a band-aid. CLAUDE.md's
no-workarounds rule says we should retire it.
The proper fix is switching our EnvCell rendering from the
general-purpose PrepareMeshDataAsync entry point (which iterates
static-object parts + emitters we don't need + triggers the buggy
TryGet<Setup> call) to WB's narrower PrepareEnvCellGeomMeshDataAsync
API at ObjectMeshManager.cs:386. That function only builds cell
room mesh — which is the only thing we use WB for at the cell
level. Static objects are already hydrated separately, particle
scripts already run via our own EntityScriptActivator.
#87 is the issue tracking that refactor. When it lands the WB fork
returns to pristine state (no acdream-specific commits on the
acdream branch for this file).
Handoff doc updated to flag the patch as a known band-aid pending #87.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Roadmap shipped-table: two new rows for Phase 1 (diagnostics) +
Phase 2 (fix). Header status block updated to 2026-05-19 with the
Phase 2 cause + fix one-liner and pointer to the 9 surfaced issues.
- ISSUES.md: filed nine new issues (#78-#86) covering the indoor
bugs the user observed once the floor rendered. Grouped under an
"Indoor walking issue cluster" header. Cross-references the Phase 1
+ Phase 2 work that surfaced them. Hypotheses + suspected root
causes documented for each.
The 9 issues split into two probable shared-cause groups:
- Cell BSP / portal cull (#78, #84, #85, #86) — likely fixable in
one phase.
- Indoor lighting plumbing (#79, #80, #81, #82) — needs separate
investigation per-symptom.
Plus #83 (stairs) which probably needs its own physics phase work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mark #61 DONE inline with the resolution writeup, including the
widened scope note that the same link→cycle boundary bug also caused
the local-player run-stop twitch the user observed during the M2
anim-pass session.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Moves #77 from Active to Recently closed with the resolution writeup
(both halves — walk-vs-run misclassification + velocity-leak in
turn-in-place — plus the decomp anchors and verification record).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#77 captures two close-range visual bugs observed during Step 2 verification
that are pre-existing — not caused by 0b25df5. Both live in
PlayerMovementController.DriveServerAutoWalk / threshold logic from
Phase B.6 work (#75). Trace evidence captured from launch.log of the
verification run.
#76 closed with the diag-trace evidence: identity invariants matched
across session / _liveSession / _liveSessionController.Session
(hashcode=1034657 throughout), so the original suspected bug-class was
wrong. The first Step 2 attempt's apparent regression was almost
certainly a stale ACE session.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A first attempt at extracting LiveSessionController out of GameWindow.cs
(Step 2 of docs/architecture/code-structure.md §4) was implemented and
reverted in the same session after visual verification at Holtburg
exposed three regressions: chat input field accepted text but nothing
was sent; door/NPC double-click fired the Use packet outbound but had
no visible client-side effect; R + click-target auto-walk no longer
fired the deferred Use on arrival (re-opens issue #63 / #75 territory).
Filing as a tracked open issue so the next refactor attempt has a
clear root-cause-investigation starting point with four hypotheses to
test. Step 1 (eda936d) and the Rule 5 follow-up (32423c2) remain
clean and verified.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes#63, #69, #74, #75. Replaces the chain of Commit-B workarounds
that compensated for ACE's MoveToChain getting cancelled by a leaked
user-MoveToState packet during inbound auto-walk. The fix is
architectural — auto-walk drives the body directly from the
server-supplied path data, no player-input synthesis, no spurious
wire-packet transitions, no grace-period band-aid.
Architectural change (closes#75):
PlayerMovementController.ApplyAutoWalkOverlay → DriveServerAutoWalk.
- Steps Yaw toward target at retail-faithful turn rates.
- Computes desired forward velocity from path runRate.
- Calls _motion.DoMotion(WalkForward, speed) directly for the
motion-interpreter state (drives animation cycle).
- Sets _body.set_local_velocity directly when grounded.
- Returns true to gate the user-input motion + velocity section
in Update so user-input flow doesn't overwrite auto-walk
velocity or motion state.
Mirrors retail's MovementManager::PerformMovement case 6 (decomp
0x00524440) which never touches the user-input pipeline during
server-controlled auto-walk.
Wire-layer guard at GameWindow.cs:6419 retained as a SEMANTIC
statement (`if (result.MotionStateChanged && !IsServerAutoWalking)`):
user-MoveToState packets are for user-driven motion intent. During
server-controlled auto-walk, the motion-state transitions caused by
the animation override (RunForward / WalkForward / TurnLeft /
TurnRight cycles) must not leak as user-cancellation packets. This
is NOT the deleted 500ms grace-period band-aid; it's the wire-layer
expressing the user-vs-server motion split.
Animation plumbed for auto-walk phases (closes#69):
- Moving forward → WalkForward (speed=1.0) / RunForward (speed=runRate)
- Turn-first phase → TurnLeft / TurnRight (sign of yawStep)
- Aligned-but-pre-step / arrival → no override (idle)
Driven via _autoWalkMovingForwardThisFrame + _autoWalkTurnDirectionThisFrame
fields set in DriveServerAutoWalk and read in the MovementResult
construction at the bottom of Update. UpdatePlayerAnimation picks up
the localAnimCmd as the highest-priority animation source.
Walk/run threshold = 1.0m, retail-observed. ACE's wire-default of
15.0f is too generous; ACE's own physics layer uses 1.0f at
MovementParameters.cs:50 (with the 15.0f line commented out) and
Creature.cs:312 notes "default 15 distance seems too far". The
formula matches retail's MovementParameters::get_command at decomp
0x0052aa00: running = (initialDist - distance_to_object) >=
threshold, evaluated ONCE at chain start and held for the rest of
the auto-walk (matches retail "runs all the way / walks all the way"
behaviour). Wire-supplied threshold is ignored.
Pickup gate (IsPickupableTarget) now uses BF_STUCK
(acclient.h:6435, bit 0x4) to discriminate immovable scenery from
real pickup items that share a Misc ItemType. Sign (pwd=0x14 with
BF_STUCK) → blocked; spell component (pwd=0x10, no BF_STUCK) →
allowed. ACE's PutItemInContainer (Player_Inventory.cs:831-836)
responds with WeenieError.Stuck (0x29) on stuck items so the gate
prevents wasted wire packets + a UX dead-end.
R-key dispatch by target type. UseCurrentSelection's top-level
IsUseableTarget gate was wrong (blocked USEABLE_NO=1 items that
ARE pickupable). Reordered:
1. Creature → SendUse
2. Pickupable → SendPickUp
3. Useable → SendUse
4. Otherwise → "cannot be used" toast
Each handler keeps its own gate. Matches retail's per-action
server-side validation.
AP cadence revert (closes#74). With the MoveToChain race fixed,
the per-frame "send while moving" cadence is no longer load-bearing.
Reverted to retail's two-branch ShouldSendPositionEvent gate
(acclient_2013_pseudo_c.txt:700233-700285):
Interval NOT elapsed (< 1 sec): send if cell or contact-plane changed.
Interval elapsed (>= 1 sec): send if cell or position frame changed.
Adds _lastSentContactPlane field + ApproxPlaneEqual helper +
PlayerMovementController.ContactPlane public accessor. Extended
NotePositionSent(Vector3, uint, Plane, float) — both outbound sites
(MoveToState + AP) pass _playerController.ContactPlane.
Effective rates: 0 Hz idle, ~1 Hz smooth motion, per-event on
cell/plane changes, 0 Hz airborne.
CLAUDE.md updated with no-workarounds rule (commit `da126f9` on
the worktree branch). Saved as feedback memory at
memory/feedback_no_workarounds.md.
Tests: build green; Core.Net 294/294; Core 1073/1081 (baseline,
8 pre-existing Physics failures unchanged). Visual-verified
end-to-end on 2026-05-16 for far/near Use + PickUp on NPCs,
doors, items, spell components, signs (correctly blocked), corpses,
turn-first animation, run/walk thresholds, idle quiet, smooth-
motion 1Hz.
Spec: docs/superpowers/specs/2026-05-16-phase-b6-suppress-movetostate-during-inbound-autowalk-design.md
Plan: docs/superpowers/plans/2026-05-16-phase-b6-suppress-movetostate-during-inbound-autowalk.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Retires divergences flagged in the 2026-05-16 faithfulness audit:
1. AP cadence. Replaces the 1 Hz idle / 10 Hz active flat heartbeat
with a diff-driven model gated on `Contact && OnWalkable`
(acclient_2013_pseudo_c.txt:700327 SendPositionEvent). Sends on
position or cell change while grounded on walkable, plus a 1 sec
heartbeat; suppressed entirely airborne. PlayerMovementController
exposes `NotePositionSent(pos, cellId, now)` which GameWindow stamps
after each AutonomousPosition / MoveToState send — mirrors retail's
shared `last_sent_position_time` between SendPositionEvent
(0x006b4770) and SendMovementEvent (0x006b4680). Known divergence
from retail: ours is per-frame-while-moving, retail's effective rate
is ~1 Hz during smooth motion (cell/plane checks). Filed as #74,
blocked by #63 — when #63 lands we revert to retail's narrower gate.
2. Workaround retirement. Removes TinyMargin (0.05 m inside arrival)
and the AP-flush before re-send (`SendAutonomousPositionNow`). The
diff-driven cadence makes both obsolete. Close-range turn-first
deferred Use is kept (it IS retail — ACE Player_Move.cs:66-87
mirrors retail's CreateMoveToChain pre-callback rotation), renamed
`OnAutoWalkArrivedSendDeferredAction` to clarify it's a FIRST send.
`isRetryAfterArrival` parameter dropped.
3. Far-range Use/PickUp retry. Restored — was load-bearing, not the
"redundant cleanup" the Group 2 audit thought. Issue #63 means ACE
drops the first Use as too-far without re-polling on subsequent APs;
the arrival re-send is what makes far-range Use complete. Logs
include `(queued for arrival re-send pending #63)` to make this
explicit. Removes when #63 closes.
4. Screen-rect picker. New `AcDream.Core.Selection.ScreenProjection`
helper shared by `WorldPicker` and `TargetIndicatorPanel`. The
`Setup.SelectionSphere` projects to a screen-space square (retail
anchor `SmartBox::GetObjectBoundingBox` 0x00452e20); picker
hit-tests the mouse pixel against the same rect the indicator draws,
inflated by 8 px (`TriangleSize`). Guarantees what-you-see is
what-you-click — including rect corners that were dead zones under
the old ray-sphere picker. Per-type radius (1.0/1.6/2.0 m) and
vertical-offset (0.2/0.9/1.0/1.5 m) heuristic lambdas retired;
`IsTallSceneryGuid` deleted; `EntityHeightFor` trimmed to 1.5 m × scale
defensive default. No defensive sphere synth — entities without a
baked `SelectionSphere` are skipped, matching retail's
`GfxObjUnderSelectionRay` (0x0054c740).
5. Rotation rate run multiplier (Commit A precursor). `TurnRateFor(running)`
helper applies retail's `run_turn_factor = 1.5f` (PDB-named
0x007c8914) under HoldKey.Run, matching `apply_run_to_command` at
0x00527be0 (line 305098). Effective: walking ≈ 90°/s, running ≈ 135°/s.
Keyboard A/D + ApplyAutoWalkOverlay both use it.
6. Useability gate (Commit A precursor). `IsUseableTarget` corrected to
`useability != 0` per `ItemUses::IsUseable` at 256455 — ANY non-zero
passes (USEABLE_NO=1, USEABLE_CONTAINED=8, etc.), not just the
USEABLE_REMOTE bit. Cross-checked against 4 call sites in retail
(ItemHolder::UseObject 0x00588a80, DetermineUseResult 0x402697,
UsingItem 0x367638, disable-button-state 0x198826). Added
`ProbeUseabilityFallbackEnabled` diagnostic
(`ACDREAM_PROBE_USEABILITY_FALLBACK=1`) to measure how often the
creature/BF_DOOR fallback fires for ACE-seed-DB entities with
null useability.
CLAUDE.md updated with the graceful-shutdown rule for relaunch:
Stop-Process bypasses the logout packet, leaving ACE's session marked
logged-in for ~3+ min. CloseMainWindow() sends WM_CLOSE so the
shutdown hook runs and the logout packet reaches ACE.
Tests: +3 ScreenProjectionTests + 6 WorldPickerRectOverloadTests = +9.
Core.Net 294/294 pass; Core 1073/1081 (8 pre-existing Physics failures
unchanged). Visual-verified 2026-05-16: rotation rate, useability,
screen-rect click area, double-click + R-key + F-key Use/PickUp at
short and long range — dialogue/door/pickup fire on arrival.
Filed follow-ups #70 (triangle apex/size DAT sprite), #71 (picker
Stage B polygon refine), #72 (cdb omega.z probe), #73 (retail-message
sweep pattern), #74 (per-frame AP chattier than retail — blocked by
#63). Old ray-sphere `WorldPicker.Pick(origin, direction, ...)`
overload kept for back-compat; no callers in acdream proper.
Plan: docs/superpowers/plans/2026-05-16-retail-faithfulness-fixes.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User report: 'You should be face to face with the NPC before sending
use. So first is rotation, when you are facing, then using.' and
'it does not face it completely.'
Two changes:
1. Split alignment thresholds in ApplyAutoWalkOverlay:
walkAligned (30°) — gate for synthesised Forward+Run motion
during far-range approach; body walks
while finishing residual turn within 30°.
aligned (5°) — gate for arrival-fire. Final facing
before the auto-walk ends and the action
re-sends. Matches retail's tight pre-Use
rotation tolerance.
Within-arrival check still requires alignment; without alignment
the body holds in turn-only mode regardless of distance.
2. Defer wire Use/PickUp packet for CLOSE-range targets. SendUse
and SendPickUp now check IsCloseRangeTarget(guid): if the player
is already within the target's use-radius, we install the
speculative overlay, set _pendingPostArrivalAction, and RETURN
without sending the wire packet. AutoWalkArrived fires after the
local rotation completes (alignment within 5°); the existing
re-send handler then fires SendUse with isRetryAfterArrival=true,
sending the wire packet at that moment. Effect: rotate first,
THEN Use — the NPC/door/item only sees the action after the
character has turned to face it.
Far-range path unchanged: send immediately, ACE auto-walks,
arrival re-sends.
Filed #69: turn animation (leg/arm cycle while pivoting). The body
now rotates but doesn't play the TurnLeft/TurnRight cycle the user
wants to see. Separate scope — needs motion-interpreter integration.
10 Hz heartbeat (301281d) made ACE see us in-radius before its
MoveToChain timeout; user confirmed doors work now. Closing #67 — root
cause was 1 Hz position outbound on our side, not anything door-
specific. Same fix unblocked door + NPC.
Two visible refinements:
1. Turn-first gate. User report: 'when I use from far range, I should
face that object and then start moving. Now it starts running
before facing is complete.'
ApplyAutoWalkOverlay now suppresses Forward motion when the
heading delta to the target is > 30°. Body turns IN PLACE first,
then walks forward once roughly aligned. Within the 30° band the
body walks while finishing the residual turn. Matches the user-
observed retail rhythm.
2. Arrival margin shrunk 0.2 m → 0.05 m. User report: 'NPC dialogue
fires, but still a bit too close. In retail it fires from a longer
range.' With the 10 Hz heartbeat the server-side Player.Location
tracks us within ~100 ms, so the bigger safety margin is no longer
needed — only a tiny epsilon to absorb the sub-tick race between
local arrival fire and the next outbound packet.
Filed #68: remote players' running animation doesn't transition to
Ready on auto-walk arrival when observed from acdream. Separate
visual bug — server-side action completes correctly; just the cycle
on the dead-reckoned remote body doesn't flip back to idle.
Margin trim:
Previous: min(0.5, threshold * 0.4) — for 3 m NPC arrived at 2.5 m
New: min(0.2, threshold * 0.2) — for 3 m NPC arrives at 2.8 m
User feedback: 'compared to retail, it fires too close. In retail
it fires from a longer range.' Smaller margin matches that — still
safely inside ACE's strict WithinUseRadius but closer to the boundary.
Tight pickup radii (0.6 m item) now arrive at 0.48 m (was 0.36 m).
Filed issues:
#67 Door Use action doesn't complete after auto-walk arrival.
NPC dialogue fires correctly post-flush-AP+re-send, but
doors still go silent — need to investigate door-specific
state requirements in ACE's Door.ActOnUse or our wire
payload differences.
#66 Rotation: local player flips back after auto-walk arrival
(observed from retail observer); NPCs don't turn to face
the player when used. Both rooted in missing MovementType=8
TurnToObject handling. Supersedes #65 (which was local-only)
with a unified rotation-handling phase scope.
Phase B.5 (ground-item pickup, close-range path) shipped and
visual-verified 2026-05-14 at Holtburg. M1 demo target 4/4 ("pick up
an item") met.
New ship-handoff doc captures the 5-commit history including the
post-visual-test PickupEvent (0xF74A) wire-handler fix that closes
the local-despawn gap.
Roadmap and CLAUDE.md updated to reflect the ship + the new follow-up
issues:
- #63 (MEDIUM) — server-initiated MoveToObject auto-walk not
honored; blocks double-click pickup + out-of-range F. Filed as
candidate Phase B.6. holtburger has the reference implementation.
- #64 (LOW) — local-player pickup animation does not render
(retail observers see it correctly). Likely a self-echo filter
dropping UpdateMotion(Pickup) on the local player.
Carry-overs from B.4c (#61 link-cycle flash, #62 PARTSDIAG null-guard)
unchanged.
Final whole-branch code review (Opus) surfaced two Important post-merge
follow-ups + a one-word inaccuracy in the handoff doc:
- #59: tighten WorldPicker per-entity Setup.Radius (M1-deferred; the
ServerGuid==0 invariant is load-bearing and worth documenting before
L.2d's CBuildingObj port lands).
- #60: port retail's obstruction_ethereal downstream path so combat-HUD
contact reporting works for ethereal creatures (M2-combat).
- handoff: corrected "Added a _entitiesByServerGuid reverse-lookup" to
"Used the pre-existing _entitiesByServerGuid" — the dict has existed
since Phase 6.6/6.7; slice 1c used it, didn't add it.
Review verdict: branch ready to merge to main.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
Roadmap:
- Status header now reads "Between phases" with C.1.5b in the recent-wins
bullet list.
- New SHIPPED row in the table (C.1.5b) summarising both slices, the
reality discovery about EnvCell statics, and the 4 visual-verified sites.
- The "IN FLIGHT — C.1.5b" sub-bullet under Phase C flipped to SHIPPED.
ISSUES.md:
- #56 removed from Active.
- #56 added to Recently closed with full commit chain (1e3c33b spec+plan,
f3bc15e SetupPartTransforms helper, 11521f4 ParticleHookSink part-transform,
5ca5827 activator refactor, 8735c39 GpuWorldState fire-sites), resolution
notes, and the visual-verification record.
CLAUDE.md:
- "Currently in flight" pointer replaced by a "Currently between phases"
marker + a C.1.5b shipped paragraph that's parallel to the existing
C.1.5a entry.
- "After C.1.5b" → "Next phase candidates".
Visual verification 2026-05-12: portal swirl matches retail extent + lateral
spread (no ground-burial); Holtburg Inn fireplace flames; cottage chimney
smoke; spell-cast particles on +Acdream — all match retail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Visual verification at the Holtburg Town network portal passed for the
slice's mechanism: 10-hook portal script fires end-to-end with correct
color, persistence, orientation, and multi-emitter dispatch. After the
334f0c6 rotation-seed fix, the swirl is oriented correctly along the
portal's facing instead of world-NS.
Known limitation surfaced during verification and filed as issue #56:
ParticleHookSink ignores CreateParticleHook.PartIndex, so all 10 of the
portal's emitters collapse to the entity root position + identity-rotated
offset, producing a compressed and partly-ground-buried swirl instead of
the multi-tier shape retail renders. Mechanism is correct; per-part
transform handling is the next vfx-pipeline concern (will affect every
multi-emitter PES — slice 2 chimneys/fireplaces in particular).
Documentation changes:
- docs/ISSUES.md: new #56 entry with the captured entity guids
(0x7A9B405B / 0x7A9B4080), script ids (0x3300126D / 0x3300067A),
symptom data, root-cause hypothesis, file pointers, and acceptance
criterion. Notes the blocks-slice-2 relationship explicitly.
- docs/superpowers/specs/2026-05-12-phase-c1.5a-portals-design.md §9:
new limitation #1 documenting the verified PartIndex collapse symptom.
- docs/plans/2026-04-11-roadmap.md: new "C.1.5a" row in the shipped
table referencing the spec, plan, and #56 caveat.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final code review of slice 1 flagged one Important issue (the spec's
"zero cost when off" claim for the surface-dump path is technically
violated — _uploadMetadata always writes one dict entry per upload
regardless of env var) plus minor doc/consistency gaps. Applied:
1. Spec §5 "Cost when off": dropped the "Zero" claim; replaced with
"Negligible — one Dictionary write per upload (~30-50 KB at Holtburg)
plus a hash-table write per upload. Expensive work (file I/O,
histogram construction) is still env-gated." This matches reality.
2. Baseline doc §5: rewrote from "Raw logs (scratch, can be deleted)"
referencing files that were never preserved in this worktree, to
"Reproducing the measurements" with the actual PowerShell launch
commands. Honest about the raw logs not being kept; the captured
medians in section 2 are the canonical record.
3. New issue #55 filed in docs/ISSUES.md — static-entity slow path
reports ~1.45M meshMissing/5s at r4 standstill, drops to ~0 when
walking. LOW severity (no visible regression), hypothesis points
at a "permanently-missing entity gets re-classified every frame"
pattern that Tier 1 cache doesn't cover.
4. Roadmap shipped table: renamed "N.6.1" row to "N.6 slice 1" to
match every other artifact's naming. Search-discoverability fix.
None of these change the slice's conclusion or next-phase
recommendation (C.1.5 first, then reduced-scope slice 2).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EntityClassificationCache keyed by (entityId, landblockHint) tuple lands
per spec docs/superpowers/specs/2026-05-10-issue-53-tier1-cache-design.md
+ plan docs/superpowers/plans/2026-05-10-issue-53-tier1-cache.md.
Perf result (horizon-safe preset + High quality, AMD Radeon RX 9070 XT
@ 1440p): entity dispatcher cpu_us median ~1200 us, p95 ~1500 us. Down
from ~3500m / ~4000p95 pre-Tier-1. ~66% / ~63% reduction. Well under
the A.5 spec budget (median <= 2.0 ms, p95 <= 2.5 ms). No BUDGET_OVER
flag across 30s+ standstill captures.
Visual gate cleared after 4 bug-fix iterations:
- 71d0edc: namespace stab Ids globally (cross-LB id collision)
- 95ebbf3: key cache by (entityId, landblockHint) tuple (defensive)
- c55acdc: skip cache populate when classification is incomplete
- f928e66: incomplete-entity flag must persist across same-entity tuples
User-confirmed visually via +Acdream test character: NPCs animate,
multi-part static buildings render fully (no airborne geometry, no
Z-fighting, no missing parts, no wrong textures), Nullified Statue of
a Drudge on top of the Foundry renders all parts, trees outside
Holtburg render with branches present.
Closes the post-A.5 polish phase. Issues #52, #54, #53 all closed.
Tests: 1711 passing, 8 pre-existing physics/input failures unchanged.
N.5b sentinel: 112/112 throughout.
Memory: ~/.claude/projects/.../memory/project_tier1_cache.md +
feedback_cache_per_tuple_pattern.md capture the audit-gap and per-tuple-
vs-per-entity recurring trap for future cache work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move ISSUE #54 to Recently closed referencing commit `bf31e59`. Drop
#54 from CLAUDE.md "Currently in flight" — only #53 (Tier 1 retry)
remains open in the post-A.5 polish phase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move ISSUE #52 from Active to Recently closed with full root-cause writeup
referencing commit `e40159f`. Strip lifestone reference from CLAUDE.md
"Currently in flight"; remaining post-A.5 polish scope is #53 (Tier 1
retry) + #54 (JobKind plumbing).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Document Phase N.5b shipping (terrain on the modern rendering path via
Path C — `TerrainModernRenderer` mirrors WB's `TerrainRenderManager`
pattern but consumes acdream's `LandblockMesh.Build` so retail's
`FSplitNESW` formula stays in lockstep with physics + visual mesh).
Changes:
- `docs/plans/2026-04-11-roadmap.md` — add N.5b row to the Shipped
table; promote N.5b's "Phases ahead" entry to ✓ SHIPPED with the
Path C resolution + perf reality check; refresh N.6 scope to note
Terrain has joined the modern path (legacy `Texture2D` retirement
scope narrows to Sky + Debug); update top-of-doc Status line.
- `docs/ISSUES.md` — close issue #51 (WB terrain-split formula
divergence). Move from OPEN to "Recently closed" with the Path C
resolution: never adopted WB's formula; modern dispatcher uses
retail's via `LandblockMesh.Build`. References `da56063` (the
black-terrain fix that landed within the N.5b ship chain).
- `CLAUDE.md` — add `TerrainModernRenderer.cs` to the WB integration
cribs list with the GL_INVALID_OPERATION caveat (use uvec2 +
`sampler2DArray(handle)` constructor, NOT direct
`uniform sampler2DArray` + `glProgramUniformHandleARB`). Update
the "Currently in flight" preamble: N.6 builds on N.5 + N.5b;
add an N.5b shipped paragraph linking the perf baseline doc.
- `docs/plans/2026-05-09-phase-n5b-perf-baseline.md` — new doc
capturing the radius=5 Holtburg perf measurement (modern 6.4-7.0
µs median vs legacy 1.5 µs — modern is ~4× SLOWER on CPU at
radius=5). Documents the spec acceptance criterion #5 amendment,
the architectural wins that DO hold (zero glBindTexture/frame,
constant-cost dispatch as A.5 raises radius, per-LB frustum cull),
and the three high-value gotchas surfaced during implementation.
User-memory updates (outside repo, not in this commit):
- `memory/project_phase_n5b_state.md` — full N.5b state file with
the three gotchas captured.
- `memory/MEMORY.md` — index entry pointing at the state file.
Build: dotnet build green. No code changes in this commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final cross-cutting review of N.5 found that Task 15's deletion of
mesh_instanced.vert/.frag left InstancedMeshRenderer orphaned —
ACDREAM_USE_WB_FOUNDATION=0 silently rendered terrain+sky only with
no entities. The SHIP commit's "[x] ACDREAM_USE_WB_FOUNDATION=0 still
works" claim was inaccurate.
Resolution: formal retirement of the legacy renderer path within N.5
instead of deferring to N.6.
Deleted:
- src/AcDream.App/Rendering/InstancedMeshRenderer.cs
- src/AcDream.App/Rendering/StaticMeshRenderer.cs
- src/AcDream.App/Rendering/Wb/WbFoundationFlag.cs
GameWindow simplified — capability detection is unconditional, missing
bindless throws NotSupportedException with a clear message at startup.
WbDrawDispatcher + mesh_modern shader load are mandatory after init.
No escape hatch.
GpuWorldState simplified — WbFoundationFlag.IsEnabled guards on
AddLandblock/RemoveLandblock removed; adapter calls are unconditional
when the adapter is non-null.
PendingSpawnIntegrationTests updated — WbFoundationFlag.ForTestsOnly_ForceEnable
static ctor removed (flag is gone; adapter calls are unconditional).
The ApplyLoadedTerrain physics-data loop was also simplified: the
EnsureUploaded sub-loop that fed InstancedMeshRenderer is gone;
_pendingCellMeshes is now explicitly cleared to prevent unbounded
accumulation (the worker thread still populates it, but WB handles
EnvCell geometry through its own pipeline).
Spec §2 Decision 5 + §10 Out-of-Scope updated. Plan ship-amendment
section added. Roadmap updated (N.5 ships with retirement; N.6 scope
narrowed to perf-only). CLAUDE.md "WB integration cribs" updated.
Perf baseline doc updated. WbDrawDispatcher class summary docstring
corrected to describe the as-shipped SSBO + multi-draw-indirect path.
ISSUES.md #51 updated (terrain not in N.5 scope; deferred to N.7).
Bindless support is now a hard requirement. Modern desktop GPUs
universally expose GL_ARB_bindless_texture + GL_ARB_shader_draw_parameters;
if a user hits the NotSupportedException, that's a real bug report
worth investigating, not a silent fallback.
Build: 0 errors, 0 warnings. Tests: 71/71 (Wb+MatrixComposition+TextureCacheBindless filter).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Audit during N.3 follow-up uncovered that WB's TerrainUtils
CalculateSplitDirection uses a different math expression than
retail's FSplitNESW (the AC2D-cited polynomial 0x0CCAC033 etc that
our visual terrain mesh and physics already share). Substituting
TerrainSurface.SampleZ with WB's GetHeight in isolation would
re-introduce the triangle-Z hover bug from earlier work — physics
and visual mesh would pick different diagonals on disputed cells.
Updates:
- ISSUE #51 documents the divergence with file references and the
research that's needed when N.5 picks this up.
- Roadmap N.2 entry flags the dependency on N.5 and the reasoning
("not low-risk after all").
N.1's conformance proved slope-filtering equivalence (boolean
walkable verdict), not formula equivalence. The lesson is captured
in memory (feedback_wb_migration_formulas.md, not in-repo).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds Phase N.1 to "Phases already shipped" table at top of roadmap,
updates the Phase N section to mark N.1 with checkmark SHIPPED status,
and files the known road-edge-tree cosmetic difference at landblock
0xA9B1 in ISSUES.md as issue #50 follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes#48. Trees on sloped cells visibly hovered above the visible
terrain because GameWindow.SampleTerrainZ (the bilinear fallback used
during scenery hydration before physics registers a landblock) had
its diagonal arms swapped — used the SEtoNW triangle test on SWtoNE
cells and vice versa. The ACDREAM_DUMP_SCENERY_Z=1 diagnostic showed
every scenery line ran through the bilinear path (streaming race),
so on hilly terrain scenery was placed at a Z up to ~1.5 m off from
the visible mesh.
Latent since ff325ab (2026-04-17 "feat(ui): debug overlay + refined
input controls" carrying along the upgrade). That commit reached for
WorldBuilder TerrainUtils.GetHeight as the secondary oracle and
re-derived the triangle-pair tests; the named-retail / ACE algorithm
in TerrainSurface.SampleZ (used by the physics path / player Z) was
always correct, so player feet stayed flush — the two paths just
disagreed and only scenery noticed.
Fix:
- TerrainSurface.InterpolateZInTriangle (private static) — single
source of truth for the triangle pick + barycentric Z, sourced
from FUN_00532a50 / ACE LandblockStruct.ConstructPolygons.
- TerrainSurface.SampleZFromHeightmap (public static) — heightmap-
byte-array variant for the scenery hydration fallback. Both this
and TerrainSurface.SampleZ (instance) now delegate to the same
InterpolateZInTriangle.
- GameWindow.SampleTerrainZ — thin wrapper over the new static.
- TerrainSurfaceTests.SampleZFromHeightmap_AgreesWithInstance_AcrossWholeLandblock
asserts both sampler paths agree at 1500 sample points across both
diagonals, so future drift gets caught.
The ACDREAM_DUMP_SCENERY_Z=1 diagnostic in BuildSceneryEntitiesForStreaming
is kept committed (env-var gated, zero cost when off) — useful for
the related #49 scenery (X, Y) placement investigation filed in the
same commit.
Visual verified at Holtburg landblock 0xA9B30001 2026-05-06: the
formerly floating 32 m pines (setups 0x020002D3 / 0x020002D9) now
sit flush on the visible terrain mesh.
Test baseline: dotnet test reports the same 8 pre-existing motion /
BSP step-up failures as the handoff doc warned about — no new
failures introduced.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Keep local physics authoritative at the retail 30 Hz MinQuantum, but expose a render-only position that lerps between completed physics ticks for the player mesh and chase-camera target. Network outbound continues to use the discrete physics position.
Also make the visually confirmed #47 humanoid close-detail DIDDegrade path default-on, with ACDREAM_RETAIL_CLOSE_DEGRADES=0 left as a diagnostic opt-out.
Verification: dotnet build AcDream.slnx -c Debug; focused #38 interpolation tests passed; visual confirmed smooth 2026-05-06. Full dotnet test AcDream.slnx -c Debug --no-build still has the known 8 AcDream.Core.Tests baseline failures.
Co-authored-by: Codex <codex@openai.com>
A few specific scenery GfxObjs render with their trunk base ~0.5-1.5m
above the terrain mesh while the vast majority sit flush. Per-GfxObj-
id ⇒ deterministic across instances. Player Z snap is unaffected.
Side-by-side with retail confirmed the same species place flush there.
Filed with three competing hypotheses: per-GfxObj origin convention
(some tree meshes authored with origin at bbox-center vs trunk-base),
physics-vs-bilinear terrain Z mismatch on NE↔SW-cut cells, or the
same DIDDegrade close-detail story as #47 applied to scenery.
Detailed cut-and-paste handoff for the next agent at
docs/research/2026-05-06-issue-48-handoff.md — covers the diagnostic
dump that disambiguates the hypotheses with one log line per
offending tree.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Humanoid bodies (Setup 0x02000001 + heritage variants) rendered visibly
flat / bulky vs retail because we drew the base GfxObj id from Setup /
AnimPartChange directly. Retail's CPhysicsPart::LoadGfxObjArray
(0x0050DCF0) treats that base id as the entry point to a DIDDegrade
table; close/player rendering uses Degrades[0].Id, which is the
higher-detail mesh that carries bicep / deltoid / shoulder geometry.
ACViewer also has this bug — it was the key signal it isn't acdream-
specific. Both clients drew the LOD-3 base mesh (e.g. 14 verts / 17
polys for Aluvian Male upper arm 0x01000055), missing the close-
detail variant (0x01001795: 32 verts / 60 polys).
Adds GfxObjDegradeResolver that walks the table with safe fallbacks
at every step. Wired in GameWindow after AnimPartChange application
and before texture-change resolution so texture overrides match the
resolved mesh's surfaces. Gated by ACDREAM_RETAIL_CLOSE_DEGRADES=1
and scoped to humanoid setups (34 parts with >=8 null-sentinel
attachment slots) while the fix bakes — the change is harmless on
non-humanoid setups (resolver falls back to base when no degrade
table) but we hold the broader sweep until LOD distance plumbing
lands.
User confirmed visually 2026-05-06: bicep, deltoid, and back-muscle
definition match retail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Filed #47 in docs/ISSUES.md — humanoid characters using Setup 0x02000001
(players + Woodsman + other Aluvian NPCs) render visibly bulkier and less
shape-defined than retail's view. Drudges and other monster setups render
identically. Independent of equipment (naked +Je still shows it).
Investigation this session ruled out 0xF625 ObjDescEvent drops (real bug,
fixed in e471527, but doesn't explain shape), HiddenParts overlap,
ParentIndex walking (animation frames are setup-root coords already),
and player-specific data flow (NPCs using same setup affected too).
Diagnostic infrastructure landed alongside the issue (env-var-gated, no
runtime cost when off):
- ACDREAM_DUMP_CLOTHING=1 now also prints:
- setup.Parts.Count, flatten.Count, APC count on header
- ParentIndex[] and DefaultScale[] arrays
- IdleFrame per-part Origin + Orientation (first 17 parts)
- per-part EMIT line: gfx, subMeshes count, triangle count
- TOTAL triangle / meshRef counts per entity
This is what nailed down "all 34 parts emit" + "animation frames are
setup-root not parent-local" + "humans get setup-wide 180°-Z rotation
that drudges don't" — saved hours next session.
Open hypotheses for #47 next session: per-face vs smoothed vertex
normals (per-vertex normals from dat may be face-style for human
GfxObjs but smooth for monsters), low cell ambient leaving back faces
flat-shadowed, missing MSAA on the GL window.
#45 — closed by commit e9e080d. PlayerMovementController hands a raw
localAnimSpeed (1.0 slow / runRate fast); UpdatePlayerAnimation now
scales sidestep cycles by WalkAnimSpeed/SidestepAnimSpeed × 0.5 to
match ACE's BroadcastMovement formula. User-verified.
#46 — filed. Retail clients observing acdream's local +Acdream
character see visibly blippy / laggy movement. Local acdream view of
the same character is fine; acdream observing retail-driven
characters is also fine (after #39 / #45). The degradation is
specifically on the OUTBOUND path. Likely culprits ranked: AutoPos
heartbeat cadence (acdream's fixed 200 ms is suspect per
project_retail_motion_outbound memory), MoveToState send conditions,
sequence counters, or absent HasVelocity on UPs. Verification approach
documented (two retail clients + one acdream side-by-side; cdb
breakpoint count of MovementManager::unpack_movement on retail
observer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#45 — local +Acdream slow-strafe walking renders too slow. User
observed during fix#5 visual verify of #39: the observer-side fix
landed, then the user noticed the matching animation on the local
player was also playing at sub-walk cadence. Likely the same
SidestepAnimSpeed (1.25) vs WalkAnimSpeed (3.12) mismatch as fix#5
but on the local UpdatePlayerAnimation path. Filed for separate
investigation.
#39 — added "Progress 2026-05-06" section listing the five-commit
fix sequence (8fa04af → 863d96b → bb026b7 → 2653b30 → cc62e1c →
349ba65), the wire-level finding that retail genuinely does NOT
broadcast on Shift toggle (refuting the earlier confused trace
analysis), the user-verified working cases (1/2/4/5) and the
residual items (latency from 500ms UM grace, direction-flip cases
3/6/7 not yet explicitly verified).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Visual-verify of commit 8fa04af in launch-39-candidate.log refutes the
static-analysis hypothesis that retail does not broadcast UMs on
HoldKey-only changes. The log shows:
- [FWD_WIRE] for retail actor 0x50000001 contains many direct Walk↔Run
transitions (0x44000007 ↔ 0x45000005). ACE IS sending UMs on Shift
toggle.
- [SETCYCLE] fires correctly per UM; Sequencer.CurrentMotion cycles
through Walk / Run / Turn / Sidestep correctly per [UM_RAW].
- [UPCYCLE_PLAYER] never fired — UM grace correctly suppressed it
(UMs at >2 Hz, well within 500 ms grace).
- User reports legs visually stuck in walking animation despite the
wire/sequencer saying Run.
Conclusion: bug is downstream of Sequencer.CurrentMotion — same as
2026-05-03 hypothesis F. Most likely _currNode lands on the walk-to-run
transition link after SetCycle (`currNodeIsCyclic=False` confirmed in
[SCFULL]) and Advance does not progress past it to the cycle.
The candidate fix code (LastUMTime, ApplyPlayerLocomotionRefinement,
hysteresis constants, un-gated call site) is left in place — harmless
because UM grace blocks the velocity-fallback path while UMs arrive,
and the infrastructure may be useful for cases #2–#7 if those need
velocity fallback. But it does not close case #1.
Updates ISSUES.md #39 with refuted hypothesis + new evidence + next
step pointer. findings-static.md gains "Visual-verify result" §
documenting the diagnostic dump and recommending the next investigation
target be AnimationSequencer.Advance queue-handling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a player-remote velocity-fallback path to ApplyServerControlledVelocityCycle
so that when retail (the actor) toggles Shift while holding W and acdream is
the observer, the visible leg cycle switches Run↔Walk within ~200–500 ms even
though no fresh UM arrives. Static analysis (ACE GameActionMoveToState +
MovementData.cs auto-upgrade + acdream's prior diag traces) suggests retail
does NOT broadcast a fresh MoveToState on HoldKey-only changes — acdream's
UMs handle direction-key changes and our local +Acdream's transitions, but
retail-driven actors leave the cycle stuck.
Changes (all in src/AcDream.App/Rendering/GameWindow.cs):
- New RemoteMotion.LastUMTime field, stamped in OnLiveMotionUpdated
- ApplyServerControlledVelocityCycle: removed inner IsPlayerGuid gate;
routes player remotes to new ApplyPlayerLocomotionRefinement
- ApplyPlayerLocomotionRefinement (forward-direction only):
- 500 ms UM grace window (UMs win when fresh)
- Forward-direction-only (low byte 0x05 / 0x07)
- Hysteresis: Run → Walk demote at < 4.5 m/s; Walk → Run promote > 5.5 m/s
- Skip SetCycle when neither motion ID nor speedMod changed meaningfully
- [UPCYCLE_PLAYER] diag gated on ACDREAM_REMOTE_VEL_DIAG=1
- Outer call site in OnLivePositionUpdated un-gated (!IsPlayerGuid removed);
per-remote routing now lives inside the function
Scope: case #1 (Run↔Walk forward) only. Cases #2–#7 (backward, sidestep
speed-buckets, direction-flips) remain deferred — PlanFromVelocity is
forward-only and its NPC-tuned thresholds (RunThreshold=1.25) do not
separate player Walk (~2.5 m/s) from player Run (~9 m/s); a TTD trace
of retail's per-direction algorithm should ground the wider fix.
ISSUES.md #39 updated with progress; investigation-prompt.md and a new
findings-static.md committed under
docs/research/2026-05-06-locomotion-cycle-transitions/ (the prompt was
authored on a parallel branch in commit 7a38da3 and is brought into this
worktree here so the next session can find it without branch-hopping).
Build clean. The 8 pre-existing test failures on this branch
(BSPStepUpTests.C3_Path6_AirborneMoverHitsSteepSlope, MotionInterpreter
WalkBackward GetMaxSpeed, etc.) are unrelated to this change — verified
by running them with the diff stashed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#42 — moved from OPEN to DONE in place (rich investigation log preserved
below the new Resolution block). The originally-listed mechanisms (H1
slope-driven AdjustOffset projection, H2 step-down probe, H3 EdgeSlide)
were all RULED OUT by the first evidence run; root cause was self-
collision in FindObjCollisions, not in-sweep mechanism choice. Added
forward-pointer to retail's CObjCell::find_obj_collisions self-skip
(named-retail acclient_2013_pseudo_c.txt:308931).
#43 — new entry in Recently closed for the slope staircase on grounded
player remotes. Diagnosis: PositionManager.ComputeOffset's seqVel-only
fallback returned flat-Z motion because anim cycles bake Z=0 body-local,
producing visible 5 Hz Z stepping at the server-UP cadence. Fix: project
the fallback onto the local terrain plane (mirrors retail's
CTransition::adjust_offset contact-plane projection at the queue-empty
boundary). Verified via 9193 queue-empty-with-non-zero-offset.Z ticks
across a 34m vertical traversal.
Both diagnostic env-vars kept in tree for future regression hunts:
ACDREAM_AIRBORNE_DIAG=1 and ACDREAM_SLOPE_DIAG=1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings the elated-aryabhata-208d5e branch into main. 7 commits implementing
the L.3 retail-faithful remote-entity motion port:
de129bc M1 Fresh InterpolationManager port + retail spec
40d88b9 M2 Queue routing for player-remote UPs + entity-position sync
2365c8c M3 Animation root motion fallback for idle queue
d57ace0 M6 Cleanup — dead fields + stale env-var references
c26bbbb M4 Jump-CellId fix + #42 filed
b37b713#42 root cause confirmed via A/B test
5cc2812 Handoff prompt for #42 PhysicsEngine investigation
User-verified visual checks: smooth body chase on running/walking/strafing,
no per-UP rubber-band, no slope staircase, NPCs pathing correctly, jumps
land cleanly. Two follow-up issues filed:
#41 sub-decimeter steady-state blips (velocity-synthesis residual; LOW)
#42 airborne XY drift on jumps (PhysicsEngine.ResolveWithTransition
depenetration; root cause confirmed; deep-dive prompt at
docs/research/2026-05-05-issue-42-handoff.md)
Replaces the env-var-gated experimental path (ACDREAM_INTERP_MANAGER=1)
which was marked DO-NOT-ENABLE — the env-var no longer toggles anything.
NPCs and airborne player remotes still use the legacy path; only grounded
player remotes route through the new retail-faithful queue.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A/B-tested 2026-05-05 with user observing retail-controlled remote:
- With CellId fix removed: jumps render with geometrically-correct
XY (no drift) but body falls through the floor.
- With CellId fix applied: jumps land cleanly but arc shows ~1 m
horizontal offset; snaps back on next UM.
Confirms the drift originates inside ResolveWithTransition, not from
wire data, local Euler error, or stale velocity. CellId fix kept in
place because falling through the floor is more disruptive than
~1 m visual jitter that resolves on next input.
#42 updated with the verified diagnosis, three ranked-by-probability
hypotheses for the in-sweep mechanism (initial-overlap depenetration
along non-+Z terrain normal is the leading candidate), three matching
fix paths, and a deterministic repro recipe for the next session.
The right next step is investigating PhysicsEngine.ResolveWithTransition
and comparing against retail's CTransition::find_valid_position
(docs/research/named-retail/) — out of scope for the L.3 motion port,
files as a follow-up PhysicsEngine bug.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>