Retail's OBJECTINFO::validate_walkable @0x0050d010 initializes its return slot to OK (0x0050d025) and assigns ADJUSTED only inside the below-plane guard, immediately after the push executes (0x0050d249). The guard-fail path — grounded, OnWalkable, plane too steep — jumps past the contact write, the push, and the assignment (0x0050d1b9 -> 0x0050d251): retail deliberately IGNORES the steep plane at primary validation so the insert proceeds, the step-down phase fails on the steep landing, and the edge family produces the per-tick lateral glide. ACE flattened this into an unconditional return Adjusted (ObjectInfo.cs:169) and we inherited it; our TransitionalInsert then retried the byte-identical Adjusted forever — the user's stop-instead-of-slide. Evidence chain: the user's retail observation (the axiom), the live cdb glide profile (edge_slide/cliff_slide 594 each in lockstep, step_up 0), the D0 implementer's correct STOP (fixtures reproduced the stuck fingerprint while faithfully executing the ACE-shaped reading — refuting the reading, not the code), and the capstone byte-decode both Opus reviewers re-derived independently, including the stack-slot frame arithmetic and every ret site's eax. The conformance fixture is the live topology: flat and steep terrain triangles sharing ONE cell's diagonal (a cell-boundary face does NOT reproduce the loop — the cell-scoped primary sample never validates a neighbour's triangle — and is pinned as supplementary). Sabotage: restoring the unconditional Adjusted reds the discriminator with the exact stuck position (0.325 m lateral, 28/30 stuck ticks vs 2.602 m / 14/30 fixed; reviewer B's independent five-angle table is monotone 10-85 degrees). Stuck ticks are counted from positions so the assertion survives the eventual probe strip. In-game glide gate PASSED 2026-08-08: "Well it works, we are sliding. I cant detect any speed change from retail." Filed alongside: #347 + AD-70 (our glide alternates arm/move at half retail's per-tick rate — retail redirects within the tick; next up by user direction), AD-71 (the guard's mutable WalkableAllowance operand vs retail's fixed is_valid_walkable global — now return-value-bearing), and the reviewers' named residuals in the #345 closure entry (placement-arm flip, other-cell coverage gap, EdgeSlide-less projectiles, ACE's server-side shared misport predicting remote drift-then-snap on steep terrain). The unported IsViewer arm of validate_walkable is noted in the D0 doc. Suite: clean-room complete solution 11,271 passed / 4 skipped / 0 failed; Core assembly re-run green after the review-driven test hardening. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
17 KiB
#345 D0 — branch pin, round 2: the routing is retail-faithful; the profile contradiction stands unresolved
Order: implementation session, per docs/research/2026-08-08-345-fix-contract.md and
docs/research/2026-08-08-345-mechanism-contract.md. This session's mandate was to
find the branch the first D0 pass (docs/research/2026-08-08-345-pseudocode.md)
missed — the fix contract's premise, from the live cdb profile, is that retail's
transitional_insert reaches edge_slide/cliff_slide every tick during the glide
while ours dead-loops without ever reaching it.
Verdict: the premise's specific mechanism ("Adjusted lets the insert proceed to
step-down, ours treats it as retry-from-scratch") is REFUTED by a clean re-trace and
by two independent, empirical, byte-for-byte reproductions in a synthetic fixture.
Our TransitionalInsert/InsertIntoCell/CheckOtherCells routing is faithful to
the decompiled retail control flow for this exact topology (grounded-on-flat,
step crosses into too-steep terrain). The contradiction against the live retail
profile is real and unresolved — this session SOPS per the fix contract's explicit
fallback rather than making an unproven change.
What this session did beyond the first D0 pass
The first pass (2026-08-08-345-pseudocode.md) traced ValidateWalkable,
TransitionalInsert's switch, CheckOtherCells, ValidateTransition, and
SetSlidingNormal, and concluded retail's own algorithm converges to the same
zero-yield stop. It left one explicit open question: does retail's
CObjCell::find_cell_list/our CellTransit.FindCellSet broadphase actually
include the neighboring too-steep cell as an "other cell" to test, or does the
narrower real geometry not reach it at all — a question it said needed a live
cdb trace, not more code reading.
This session did three additional things the first pass did not:
- Read
CTransition::step_down(0x0050b2a0) in full — the functiontransitional_insert's step-down/edge-slide block actually calls beforeedge_slide. This is the literal mechanism the fix contract's hypothesis named. Confirmed:step_downsetssphere_path.step_down=1, movescheck_pos.z -= arg2, and callstransitional_insert(this, 5)recursively at the LOWERED position — but this whole apparatus is gated behindtransitional_insert's ownif (edi == OK_TS)block (pc:273191, 0x0050b787), which is reached only when both the primaryinsert_into_cellandcheck_other_cellsreturnOK_TSfor the CURRENT (un-lowered) attempt. - Read
CTransition::check_other_cells's switch (0x0050ae50, pc:272733-272753) byte-for-byte — caseCOLLIDED_TS/ADJUSTED_TS(2/3) bothreturn resultimmediately, no further cells tried, no state reset. This is a clean 4-way integer switch on a virtual call's return value — no x87, no missing flag tests, no BN garbling. High confidence in this reading. - Built two independent synthetic terrain fixtures (not committed — thrown
away after use, see below) and ran them through the real
PhysicsEngine.ResolveWithTransition→Transition.FindTransitionalPosition→TransitionalInsertpipeline with the project's own instrumentation (ACDREAM_PROBE_STEP_WALK,ACDREAM_DUMP_TRANSIT_FAIL,ACDREAM_PROBE_INDOOR_BSP,ACDREAM_DUMP_EDGE_SLIDE) to get ground truth instead of continuing to hand-derive float comparisons.
The two empirical fixtures
Both: a grounded player-flagged mover (radius 0.48, ObjectInfoState.IsPlayer | EdgeSlide, oi.OnWalkable=true, oi.Contact=true), resting on flat terrain
(N=(0,0,1)), given a diagonal (45°-ish) horizontal-only request that lands on a
terrain triangle with N.z=0.6 (< PhysicsGlobals.FloorZ=0.6642, i.e.
genuinely too steep — matching the fix contract's "N.z≈0.6" scenario).
Fixture A — steep triangle in a NEIGHBORING outdoor cell (request crosses a
24 m terrain-cell boundary). Result: the PRIMARY cell's terrain query is
out-of-bounds for the new XY (SampleTerrainWalkableInCell's fixed-cell bounds
check, PhysicsEngine.cs:1076-1080 — itself a documented, intentional retail
mirror: "retail continues dispatching the captured CObjCell*") and passes
through as OK. CheckOtherCells (TransitionTypes.cs:2999-3010) then finds
the true neighboring cell, calls ValidateWalkable, gets the below-surface
"too steep" branch (dist=-0.04, sp.StepDown=false at this point since we're
still in the un-lowered outer attempt, walkable=false since N.z(0.6) < WalkableAllowance(FloorZ=0.6642)), returns TransitionState.Adjusted
unconditionally (the branch always returns Adjusted regardless of whether the
push guard fired — ValidateWalkable, TransitionTypes.cs:3785).
ApplyOtherCellResult (TransitionTypes.cs:3316-3333) halts on Adjusted.
RunCheckOtherCellsAndAdvance returns Adjusted. TransitionalInsert
(TransitionTypes.cs:2055-2065) sees otherState != OK, sets
transitState = otherState, and continues the outer retry — never
reaching the ci.ContactPlaneValid/step-down gate at line 2248/2252 at all.
Three outer attempts, byte-identical dist=-0.04000 each time (the query
never changes because nothing in the ADJUSTED path perturbs sp.CheckPos).
ValidateTransition then forces OK, reverts to the start position: 0%
XY yield, [transit-fail] STUCK-TICK fires.
Fixture B — steep triangle WITHIN the SAME primary cell (both start and
target inside one 24 m cell's own quad; the too-steep triangle is one of the
cell's own two split triangles, N=(-0.8,0,0.6), reached via the diagonal
split so no other-cell dispatch is needed at all). Result: even cleaner —
InsertIntoCell's OWN inner retry loop (TransitionTypes.cs:2383-2390, up to
3 attempts) calls FindPrimaryCellCollisions → FindEnvCollisions →
ValidateWalkable directly, gets Adjusted on every one of its 3 attempts
(dist=-9.74400 identical all 3, deep penetration by design), and returns
Adjusted from InsertIntoCell itself — before check_other_cells is even
called. TransitionalInsert's outer switch, case ADJUSTED_TS: { neg_poly_hit = 0; break; } (TransitionTypes.cs:2030-2037), skips the entire if (edi == OK_TS) block. 3 outer attempts × 3 inner attempts = 9 total
[transit-fail-insert] lines, every one env=Adjusted, never reaching
environment=OK. DoStepDown/EdgeSlideAfterStepDownFailed are never
called — zero edge-slide: diagnostic lines fire in this fixture's log,
confirmed by grep. 0% XY yield, STUCK-TICK fires, identical fingerprint to
Fixture A and to the original captured bug (spStepDown=False,
guardPassed=False, outcome=Adjusted, byte-identical dist every attempt).
Both fixtures independently, by two structurally different routes (neighbor
cell vs. same cell), reproduce the exact captured #345 fingerprint from
docs/ISSUES.md — and both do so by faithfully executing the documented
retail control flow (check_other_cells's unconditional early return on
Adjusted; transitional_insert's post-switch edi==OK_TS gate). Neither
fixture ever reaches DoStepDown/EdgeSlideAfterStepDownFailed/CliffSlide —
which the trap-list machinery (RetailEdgeResponseOrderingTests.cs,
already green) proves is itself faithful and reachable once a clean OK
attempt occurs. The problem, if it is a problem in acdream's code at all, is
never reaching a clean OK attempt in the first place — which is exactly what
retail's own decompiled insert_into_cell/check_other_cells say should
happen for this topology too.
Why this is a STOP, not a D1 fix
The fix contract's own hypothesis was specific: "does retail's grounded step
run a step-down phase per sub-step such that validate_walkable's Adjusted-on-
steep lets the insert PROCEED to that phase ... where ours treats the
primary's Adjusted as retry-the-insert?" This session traced the exact
function (step_down) and the exact gate (transitional_insert's
if (edi == OK_TS)) the hypothesis named, byte-for-byte, and found retail's
own code does not let an Adjusted result proceed to the step-down phase —
it retries from scratch, identically to acdream's port. The hypothesis, taken
literally, is refuted, not confirmed. Two independently-constructed fixtures
running acdream's real TransitionalInsert/InsertIntoCell/CheckOtherCells
production code confirm this refutation empirically, not just on paper.
This leaves the contradiction between:
- the pseudo-C (hand-traced + empirically reproduced twice): this topology should dead-loop, in BOTH retail and acdream, and
- the live cdb profile (
345-retail-glide.cdb.log): retail's glide firesedge_slide/cliff_slide594 times each, in lockstep,step_upzero,
genuinely unresolved. Per the fix contract ("if the hypothesis is wrong, keep
going until you find the branch that produces the profile") this session kept
going — through step_down, check_other_cells, InsertIntoCell's own
retry loop, CheckOtherCells's cell-set dispatch (RunCheckOtherCellsAndAdvance,
TransitionTypes.cs:3562-3636, CellTransit.FindCellSet) — and found every
one of those to be a faithful, citable, non-garbled port. No garbled
Binary-Ninja decision point was found in the functions actually exercised by
this topology (insert_into_cell, transitional_insert's switch,
check_other_cells's switch, step_down) — all were clean integer
dispatches, not x87/flag-test mush, so this is not a "note the address and
STOP for byte-decode" situation either.
What remains unresolved, and is out of this session's scope to fix by guessing:
- Whether retail's actual Rithwic terrain topology differs structurally from
both synthetic fixtures here in some way that changes which branch fires —
e.g. a shallower approach that keeps the primary/other-cell query in the
"above" branch for more of the glide, letting more attempts reach a clean
OKand hencestep_down/edge_slide(this is plausible in principle: the live profile'sset_sliding_normalcount, 538, is lower thanedge_slide/cliff_slide's 594, meaning not everyedge_slidecall reachescliff_slide's effective branch — consistent with a live approach that spends some attempts in "above" and only some in "below-and-caught"). - Whether
CellTransit.FindCellSet's broadphase (feedingCheckOtherCells'scellSet) is over- or under-inclusive relative to retail's realCObjCell::find_cell_listfor a moving sphere near a terrain-cell boundary — the same open question the first D0 pass flagged, still open, still requiring the live cdb trace named in that pass's own recommendation (validate_walkable @0x0050d010/transitional_insert @0x0050b6f0/adjust_sphere_to_plane @0x00538210while the glide happens), which this text-only session cannot execute.
Per the fix contract's acceptance rule #4 ("Any contradiction between the profile, the pseudo-C, and our code that you cannot resolve → STOP and report with the evidence. A correct STOP is a success."), this session stops here. No production code was changed. The two throwaway diagnostic fixtures used to gather this evidence were deleted before this document was written; they are reproducible from the exact parameters quoted above if a future session wants to re-verify or extend them (in particular: a graduated multi-triangle terrain fixture that keeps some attempts in the "above" branch, to test the "live approach spends only some attempts below" theory above).
Trap list compliance
No change was made to ValidateWalkable's math, AdjustOffset, the #331
absorb, the #32 setter split, DoStepUp/DoStepDown internals, the
edge-family response bodies, or the stepDownHeight = oi.StepUpHeight
oddity. RetailEdgeResponseOrderingTests.cs (unmodified) already pins that
DoStepDown→EdgeSlideAfterStepDownFailed→CliffSlide chain as faithful and
reachable from a clean OK attempt — this session's fixtures corroborate that
finding rather than contradicting it; the gap is entirely upstream of it.
Parent addendum (2026-08-08, post-STOP): the ACE cross-check sharpens the question
references/ACE/Source/ACE.Server/Physics/Transition.cs:779-934
(TransitionalInsert) — an independent port of the same retail loop — shows
EdgeSlide is reachable ONLY from the transitState == OK arm: insert
succeeds → the grounded step-down block runs → StepDown fails →
EdgeSlide(ref transitState, ...). On Adjusted the loop merely clears
NegPolyHit and retries, exactly as our port and this doc's fixtures show.
Therefore the live profile (594 edge_slide/cliff_slide lockstep per run,
step_up=0) FORCES the conclusion that retail's primary insert RETURNS OK
per tick in the glide scenario, while ours returns Adjusted 3×3 per tick.
The missed branch is not routing-after-Adjusted; it is whatever prevents
retail's per-tick check position from tripping validate_walkable's
below-plane push (@0x0050d010 mover arm, return ADJUSTED_TS) in the
first place — most plausibly the interplay of the previous tick's
edge/cliff slide having already moved the request, or a check-position
seeding difference at insert entry.
Falsifiable predictions for the round-2 capture
(tools/cdb/345-glide-stacks.cdb):
tins(transitional_insert) counts ~1 per tick — NO retry storm in retail (ours: 9 attempts/tick).stepdownruns in lockstep withedge(≈594 scale).- The
edge_slidestacks showtransitional_insert(orfind_transitional_position) directly — the OK-arm step-down block — with NO intervening validate/adjust retry frames.
If prediction 1 fails (retail also retry-storms), the divergence is inside the retry's convergence instead, and the Adjusted-production question reopens. Either way the capture discriminates.
D0 RESOLVED (2026-08-08, parent session): the missed branch is validate_walkable's RETURN SCOPING — byte-proven
Capstone disassembly of the PDB-paired binary (C:\Users\erikn\Downloads\ acclient.exe, v11.4186) at OBJECTINFO::validate_walkable @0x0050d010:
0050d020 mov edi, 1
0050d025 mov [esp+0xc], edi ; var_1c = 1 = OK_TS (return-value slot)
...below-plane arm...
0050d1a9 test ecx, ecx ; sp->step_down
0050d1af jne 0x50d1bf ; set → guard body
0050d1b1 test byte [ebp+4], 2 ; oi->state & OnWalkable
0050d1b5 je 0x50d1bf ; clear → guard body
0050d1b7 test eax, eax ; is_valid_walkable(N)
0050d1b9 je 0x50d251 ; TOO STEEP → SKIP guard body ENTIRELY
0050d1bf ...set_contact_plane, step-down interp (fail: mov eax,2 → COLLIDED),
0050d244 call add_offset_to_check_pos ; the push
0050d249 mov dword [esp+0x10], 3 ; var_1c = ADJUSTED — ONLY after the push
0050d251 ...collision-normal tail (Contact set → skipped)...
0050d271 mov eax, [esp+0x10] ; return var_1c
The guard-fail path (grounded mover, OnWalkable, too-steep plane) never
touches var_1c: retail returns OK. The steep below-plane is deliberately
ignored by primary walkable validation; the insert proceeds, the OK-arm
step-down phase runs at the advanced position, its walkable probe fails on
the steep landing (check_walkable early-out: 0050d187 mov eax,2 —
COLLIDED, also byte-confirmed), StepDown fails, and EdgeSlide /
CliffSlide produce the per-tick lateral glide. Every round-1 counter
(594 edge/cliff lockstep, step_up=0, walkable_hits_sphere=0,
adjust_sphere_to_plane=0, vwalk high) is reproduced by this reading.
ACE misported this (ObjectInfo.cs:169 returns Adjusted
unconditionally after the guard block) and acdream inherited the shape.
Binary Ninja had var_1c = 3 correctly scoped inside the guard all along
(pc:274525+ region) — but its void return typing and bare return;
statements hid the return-value mechanics from every prior reading.
The fix (supersedes the contract's D1 scope, per its own "find the real
branch" clause): in our ValidateWalkable below-plane arm
(TransitionTypes.cs:3744-3785), return Adjusted only when the guard
passed and the push executed; return OK when the guard fails. No routing
change in TransitionalInsert is needed — the existing OK-arm step-down
block + edge family already produce the cascade (proven live-healthy by
this morning's 18 clean edge-family entries).
Also byte-confirmed while here: the step-down interp failure returns
COLLIDED (0050d28b mov eax,2) — ACE and our port are RIGHT there; BN's
bare return hid it. And the check_walkable early-out returns COLLIDED
(0050d187) — all three agree.
Flagged secondary (file, don't chase): retail's guard calls
CPhysicsObj::is_valid_walkable(N) (fixed retail threshold) at BOTH the
resting and below-plane sites; ours tests N.z >= sp.WalkableAllowance.
In this scenario both reject N.z=0.6, so it is not #345's cause — but the
operand difference needs its own conformance check.