fix(physics): TransitionalInsert returns the real exhausted-retry state

Per the TS-4/#116 oracle plan (docs/research/2026-07-30-ts4-116-oracle-plan.md
§1.4, §4 item 1): TransitionalInsert's retry loop hardcoded
`return TransitionState.Slid;` when the attempt budget exhausted, despite
the comment's own claim of returning "whatever the last iteration said."
ACE's equivalent (Transition.cs:933, `return transitState;`) and retail's
(pc:273363, 0x0050b949, `return edi;`) both reuse one state variable
across the composite per-attempt call and return whatever it holds.

acdream's per-phase dispatch (env/building/object/other-cells/neg-poly/
step-down) is split across several locals instead of ACE's single
composite call, so `transitState` is now re-synced from whichever
phase-local variable most recently caused a retry `continue`, and the
final return uses that real value instead of the hardcoded constant.

Blast radius is zero: ValidateTransition's "not OK" branch treats
Collided/Adjusted/Slid identically, and every caller of TransitionalInsert
either feeds the result straight into ValidateTransition/
ValidatePlacementTransition (both `== OK` vs. not) or checks `== OK`
directly. Full AcDream.Core.Tests suite: 4059 passed / 2 skipped, no
change in pass count.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-30 12:36:05 +02:00
parent 9e17554ed4
commit 7e1be3def0

View file

@ -1700,7 +1700,12 @@ public sealed class Transition
var ci = CollisionInfo; var ci = CollisionInfo;
var oi = ObjectInfo; var oi = ObjectInfo;
TransitionState transitState; // Initializer is unreachable in practice (numAttempts > 0 is
// guaranteed by the guard above, so the loop below always runs at
// least once and assigns transitState before any read) — required
// only to satisfy C#'s definite-assignment analysis, which can't
// connect the early-return guard to the loop bound.
TransitionState transitState = TransitionState.OK;
for (int attempt = 0; attempt < numAttempts; attempt++) for (int attempt = 0; attempt < numAttempts; attempt++)
{ {
@ -1742,6 +1747,7 @@ public sealed class Transition
if (bldgState == TransitionState.Slid) if (bldgState == TransitionState.Slid)
{ {
transitState = bldgState;
ci.ContactPlaneValid = false; ci.ContactPlaneValid = false;
ci.ContactPlaneIsWater = false; ci.ContactPlaneIsWater = false;
sp.NegPolyHit = false; sp.NegPolyHit = false;
@ -1750,6 +1756,7 @@ public sealed class Transition
if (bldgState == TransitionState.Adjusted) if (bldgState == TransitionState.Adjusted)
{ {
transitState = bldgState;
sp.NegPolyHit = false; sp.NegPolyHit = false;
continue; continue;
} }
@ -1771,6 +1778,7 @@ public sealed class Transition
// Object collision applied a push-out and set sliding normal. // Object collision applied a push-out and set sliding normal.
// Retry at the new CheckPos — we may have slid into another // Retry at the new CheckPos — we may have slid into another
// object, or need to re-verify env at the new position. // object, or need to re-verify env at the new position.
transitState = objState;
ci.ContactPlaneValid = false; ci.ContactPlaneValid = false;
ci.ContactPlaneIsWater = false; ci.ContactPlaneIsWater = false;
sp.NegPolyHit = false; sp.NegPolyHit = false;
@ -1781,6 +1789,7 @@ public sealed class Transition
{ {
// Object modified CheckPos (e.g. PerfectClip adjust_to_plane). // Object modified CheckPos (e.g. PerfectClip adjust_to_plane).
// Retry at the new position. // Retry at the new position.
transitState = objState;
sp.NegPolyHit = false; sp.NegPolyHit = false;
continue; continue;
} }
@ -1805,7 +1814,10 @@ public sealed class Transition
return TransitionState.Collided; return TransitionState.Collided;
if (otherState != TransitionState.OK) if (otherState != TransitionState.OK)
{
transitState = otherState;
continue; // ADJUSTED / SLID → retry the attempt continue; // ADJUSTED / SLID → retry the attempt
}
// ── Phase 3: both env and objects returned OK ────────────── // ── Phase 3: both env and objects returned OK ──────────────
// Handle Collide flag (BSP path 6 set it on a non-contact hit). // Handle Collide flag (BSP path 6 set it on a non-contact hit).
@ -1943,6 +1955,7 @@ public sealed class Transition
var stepUpSlideRes = sp.StepUpSlide(this); var stepUpSlideRes = sp.StepUpSlide(this);
if (stepUpSlideRes == TransitionState.Slid) if (stepUpSlideRes == TransitionState.Slid)
{ {
transitState = stepUpSlideRes;
ci.ContactPlaneValid = false; ci.ContactPlaneValid = false;
ci.ContactPlaneIsWater = false; ci.ContactPlaneIsWater = false;
continue; continue;
@ -1979,6 +1992,7 @@ public sealed class Transition
return TransitionState.Collided; // degenerate slide → hard stop return TransitionState.Collided; // degenerate slide → hard stop
// Slid / Adjusted / OK → re-test at the (slid) CheckPos, mirroring // Slid / Adjusted / OK → re-test at the (slid) CheckPos, mirroring
// retail's insert-loop continuation after slide_sphere. // retail's insert-loop continuation after slide_sphere.
transitState = slideRes;
continue; continue;
} }
} }
@ -2070,6 +2084,7 @@ public sealed class Transition
var edgeState = EdgeSlideAfterStepDownFailed(engine, stepDownHeight, zVal); var edgeState = EdgeSlideAfterStepDownFailed(engine, stepDownHeight, zVal);
if (edgeState == TransitionState.Slid) if (edgeState == TransitionState.Slid)
{ {
transitState = edgeState;
ci.ContactPlaneValid = false; ci.ContactPlaneValid = false;
ci.ContactPlaneIsWater = false; ci.ContactPlaneIsWater = false;
sp.NegPolyHit = false; sp.NegPolyHit = false;
@ -2078,6 +2093,7 @@ public sealed class Transition
if (edgeState == TransitionState.Adjusted) if (edgeState == TransitionState.Adjusted)
{ {
transitState = edgeState;
sp.NegPolyHit = false; sp.NegPolyHit = false;
continue; continue;
} }
@ -2088,9 +2104,30 @@ public sealed class Transition
return TransitionState.OK; return TransitionState.OK;
} }
// Exhausted retry attempts — return whatever the last iteration said. // Exhausted retry attempts — return the real last transition state.
// (Defaults to Slid in practice since that's the only case that retries.) //
return TransitionState.Slid; // TS-4/#116 oracle pass (Campaign P final physics slice,
// docs/research/2026-07-30-ts4-116-oracle-plan.md §1.4, §4 item 1):
// this was hardcoded to TransitionState.Slid despite the comment's
// own claim of returning "whatever the last iteration said" — a
// real, citable port-accuracy divergence from ACE's
// `return transitState;` (Transition.cs:933) and retail's
// `return edi;` (acclient_2013_pseudo_c.txt:273363, 0x0050b949),
// both of which reuse ONE state variable across the composite
// per-attempt call and return whatever it holds when the retry
// budget is exhausted. acdream's per-phase dispatch (env/building/
// object/other-cells/neg-poly/step-down) is split across several
// locals instead of ACE's single composite call, so `transitState`
// is explicitly re-synced from whichever phase-local variable most
// recently caused a retry `continue` (see the `transitState = ...`
// assignments immediately above each `continue` in this loop).
// Blast radius: zero. ValidateTransition's "not OK" branch
// (`transitionState != TransitionState.Invalid`) treats
// Collided/Adjusted/Slid identically — every caller of
// TransitionalInsert either feeds its result straight into
// ValidateTransition/ValidatePlacementTransition (both of which
// only branch on `== OK` vs. not) or only checks `== OK` directly.
return transitState;
} }
private TransitionState EdgeSlideAfterStepDownFailed( private TransitionState EdgeSlideAfterStepDownFailed(