diag(phys): [bsp-test] probe + grounded apparatus test + handoff

Visual verification of Task 7 ship: doors block at dead-center (the
small Cylinder catches) but the BSP slab doesn't catch off-center
or inside-walking-out approaches. Probe-instrumented live capture
proves multi-part registration is correct — every door spawns with
shapes=cyl1+bsp1, and the BSP part is visited 135 times for a single
door at player approaches as close as 0.42 m, with cacheHit=True.
But zero [resolve-bldg] attributions for the BSP shape.

Three artifacts added:

1. TransitionTypes.cs — new [bsp-test] probe in the BSP collision
   dispatch, fires BEFORE the cache lookup. Mirrors [cyl-test] on
   the Cylinder branch. Distinguishes "cache miss → silent skip"
   from "queried but no hit" (the latter doesn't show up in
   [resolve-bldg] which only fires on attributed hits).

2. DoorCollisionApparatusTests.cs — new grounded test
   (Apparatus_Grounded_50cmOffCenter_*) attempts to reproduce the
   production bug via a seeded PhysicsBody (Contact + OnWalkable
   + ContactPlane + WalkablePolygon). Currently doesn't reproduce
   because the apparatus's stub-terrain + synthetic-floor setup
   diverges from production's real Holtburg geometry. Captured as
   "documents-the-bug" — flip the assertion shape when the fix
   lands.

3. docs/research/2026-05-24-door-collision-task7-shipped-but-bug-remains.md
   — full session handoff. Identifies the remaining bug as a Path 5
   (Contact branch + StepSphereUp) misbehavior at thin tall
   obstacles, not in the multi-part registration we just shipped.
   Leading hypothesis: DoStepUp's downward probe finds the same
   flat floor on the OTHER side of the door (Holtburg cottages have
   no Z change between exterior and interior floor), declares
   step-up success, BSP collision returns OK, sphere walks through.
   Recommended next move: relaunch with ACDREAM_DUMP_STEPUP=1 to
   verify the hypothesis.

What this commit DOES NOT do: fix the remaining step-up bug. The
A6.P4 multi-part registration foundation is correct and stays.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-24 19:22:45 +02:00
parent ca9341c2cb
commit 163a1f0d35
3 changed files with 360 additions and 0 deletions

View file

@ -199,6 +199,104 @@ public class DoorCollisionApparatusTests
}
}
/// <summary>
/// Reproduces the LIVE bug: a grounded player (isOnGround=true with
/// seeded ContactPlane) walking off-center toward a closed door
/// passes through. The path-difference test: this hits Path 5
/// (Contact branch + StepSphereUp), while the other apparatus
/// tests above hit Path 6 (Default). If Path 5 incorrectly
/// declares step-up success the BSP collision returns OK and the
/// sphere walks through — exactly what the user reports in the
/// live Holtburg session 2026-05-24.
/// </summary>
[Fact]
public void Apparatus_Grounded_50cmOffCenter_FrontApproach_DocumentsBug()
{
if (!TryBuildScenario(out var ctx)) return;
PhysicsDiagnostics.ProbeResolveEnabled = true;
PhysicsDiagnostics.ProbeBuildingEnabled = true;
Env.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", "1");
// Synthetic floor plane at Z = 0 so the grounded sphere has a
// walkable plane to rest on. Sphere foot center starts at Z=radius
// = 0.48, head at 0.48 + 1.20 = 1.68.
var floorPlane = new Plane(0f, 0f, 1f, 0f); // z = 0 plane
var floorVerts = new[]
{
new Vector3( 0f, 0f, 0f),
new Vector3(24f, 0f, 0f),
new Vector3(24f, 24f, 0f),
new Vector3( 0f, 24f, 0f),
};
var body = new PhysicsBody
{
Position = new Vector3(12.5f, 11f, 0.48f),
Orientation = Quaternion.Identity,
ContactPlaneValid = true,
ContactPlane = floorPlane,
ContactPlaneCellId = TestCellId,
WalkablePolygonValid = true,
WalkablePlane = floorPlane,
WalkableVertices = floorVerts,
WalkableUp = Vector3.UnitZ,
TransientState = TransientStateFlags.Contact | TransientStateFlags.OnWalkable,
};
var perTick = new Vector3(0f, 0.10f, 0f);
Vector3 pos = body.Position;
uint cellId = TestCellId;
bool isOnGround = true;
bool blocked = false;
Vector3 lastNormal = Vector3.Zero;
int ticks = 0;
for (int tick = 0; tick < 30; tick++)
{
Vector3 target = pos + perTick;
var result = ctx.engine.ResolveWithTransition(
pos, target, cellId,
SphereRadius, SphereHeight,
StepUpHeight, StepDownHeight,
isOnGround,
body: body,
moverFlags: ObjectInfoState.IsPlayer | ObjectInfoState.EdgeSlide,
movingEntityId: 0);
ticks = tick;
body.Position = result.Position;
pos = result.Position;
cellId = result.CellId;
isOnGround = result.IsOnGround;
lastNormal = result.CollisionNormal;
if (result.CollisionNormalValid)
{
blocked = true;
_out.WriteLine($"Tick {tick}: BLOCKED at pos=({pos.X:F3},{pos.Y:F3},{pos.Z:F3}) normal=({lastNormal.X:F3},{lastNormal.Y:F3},{lastNormal.Z:F3})");
break;
}
}
_out.WriteLine($"Final pos = ({pos.X:F3}, {pos.Y:F3}, {pos.Z:F3}) after {ticks + 1} ticks; blocked={blocked}");
_out.WriteLine($"Grounded={isOnGround}");
// EXPECTED FAILURE (documents-the-bug): the grounded sphere walks
// straight through, reaching the far side at Y > 12.30. When the
// fix lands, flip this to Assert.True(blocked) — same shape as
// the Path-6 apparatus tests above.
PhysicsDiagnostics.ProbeResolveEnabled = false;
PhysicsDiagnostics.ProbeBuildingEnabled = false;
Env.SetEnvironmentVariable("ACDREAM_DUMP_STEPUP", null);
Assert.True(pos.Y > 12.30f,
$"This test documents the production bug. If this is failing " +
$"because the sphere now blocks, the door fix worked — flip " +
$"the assertion to Assert.True(blocked) and Assert.True(pos.Y < 12.0f). " +
$"Current pos=({pos.X:F3},{pos.Y:F3},{pos.Z:F3}) blocked={blocked}");
}
// ───────────────────────────────────────────────────────────────
// Apparatus setup
// ───────────────────────────────────────────────────────────────