test(phys): A6.P3 #98 — convert FirstCap test to documents-the-bug pattern

The previous version of LiveCompare_FirstCap_HeadHitsCottageFloor
asserted the harness matched the live cap by per-field diff, which
correctly FAILED with a clear divergence message. Converted it to the
documents-the-bug pattern matching the existing
Harness_Finding_SphereGoesAirborneAtTick1 style: passes WHILE the
harness lacks the cottage GfxObj, and will start failing when the
cottage GfxObj is added — at which point the test should be flipped
to AssertCallMatchesCapture(engine, captured).

Test name now reads as a finding:
  LiveCompare_FirstCap_HarnessMissesCottageFloorBecauseCottageGfxObjNotRegistered

Second-capture poly-dump finding (committed in the test's xmldoc): the
live cap event attributes the blocking entity as obj=0xA9B47900 — a
landblock-baked static building (the cottage GfxObj). The cottage's
floor lives in this GfxObj's polygon table as a ShadowEntry, NOT in
any of the cottage's cells. The harness's BuildEngineWithCellarFixtures
intentionally skips RegisterStairRampGfxObj today, so the cottage
floor (downward-facing polygon at world Z=94.0) isn't present — and
the harness doesn't reproduce the cn=(0,0,-1) cap.

Next-session move: extract the cottage GfxObj's full polygon list
from a focused live capture (set ACDREAM_PROBE_BUILDING=1 so the
[resolve-bldg] probe fires per-polygon during the cap), add it to
RegisterStairRampGfxObj (rename to RegisterCottageGfxObj), uncomment
the registration call. The harness should then reproduce live's
cn=(0,0,-1) — at which point the documents-the-bug test starts
failing and should be flipped to the assertion form.

Test baseline maintained: 1178 + 8 pre-existing failures (was
1172 + 8 pre-changes; added 6 tests, all pass under serial run).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-23 20:10:13 +02:00
parent 44614ab591
commit 0f2db62667

View file

@ -488,21 +488,68 @@ public class CellarUpTrajectoryReplayTests
/// bumping the cottage floor from BELOW. /// bumping the cottage floor from BELOW.
/// ///
/// <para> /// <para>
/// This is the actual #98 bug, NOT a step-up / AdjustOffset problem /// This is the actual #98 bug, NOT a step-up / AdjustOffset problem.
/// — it's a head-sphere collision against a polygon that retail /// Live capture's <c>[resolve]</c> probe pinpoints the blocking
/// doesn't have (cottage floor should be punched-through above the /// entity: <c>obj=0xA9B47900</c> — a landblock-baked static building
/// ramp). Whether the harness reproduces the cap pinpoints whether /// (the cottage GfxObj). The cottage's floor polygons live in this
/// the cottage-cell floor polygon set is the cause. /// GfxObj, registered as a ShadowEntry, NOT in any of the cottage's
/// cells. The harness's <see cref="BuildEngineWithCellarFixtures"/>
/// loads cell fixtures but does NOT register the cottage GfxObj, so
/// the harness fails to reproduce the cap — DOCUMENTED here as the
/// divergence pattern.
/// </para>
///
/// <para>
/// Documents-the-bug pattern: passes WHILE the harness lacks the
/// cottage GfxObj. When a future session adds the cottage GfxObj
/// (full polygon list extracted from the live <c>[poly-dump]</c> +
/// <c>[resolve-bldg]</c> probes), this test will start failing —
/// the signal to flip it from documenting-the-bug to enforcing-the-fix.
/// </para> /// </para>
/// </summary> /// </summary>
[Fact] [Fact]
public void LiveCompare_FirstCap_HeadHitsCottageFloor() public void LiveCompare_FirstCap_HarnessMissesCottageFloorBecauseCottageGfxObjNotRegistered()
{ {
var (engine, _) = BuildEngineWithCellarFixtures(); var (engine, _) = BuildEngineWithCellarFixtures();
var captured = LoadCapturedRecord(record => var captured = LoadCapturedRecord(record =>
record.Result.CollisionNormalValid record.Result.CollisionNormalValid
&& record.Result.CollisionNormal.Z < -0.99f); && record.Result.CollisionNormal.Z < -0.99f);
AssertCallMatchesCapture(engine, captured); Assert.NotNull(captured.BodyBefore);
var body = SeedBodyFromSnapshot(captured.BodyBefore);
var harnessResult = engine.ResolveWithTransition(
currentPos: captured.Input.CurrentPos,
targetPos: captured.Input.TargetPos,
cellId: captured.Input.CellId,
sphereRadius: captured.Input.SphereRadius,
sphereHeight: captured.Input.SphereHeight,
stepUpHeight: captured.Input.StepUpHeight,
stepDownHeight: captured.Input.StepDownHeight,
isOnGround: captured.Input.IsOnGround,
body: body,
moverFlags: (ObjectInfoState)captured.Input.MoverFlags,
movingEntityId: captured.Input.MovingEntityId);
// Live reported cn=(0,0,-1) blocking the climb at this point.
Assert.True(captured.Result.CollisionNormalValid,
"Captured record must have collisionNormalValid=true.");
Assert.True(captured.Result.CollisionNormal.Z < -0.99f,
$"Captured record must have downward collision normal; got " +
$"{captured.Result.CollisionNormal}.");
// Harness does NOT reproduce the live downward push because the
// cottage GfxObj is not registered — the blocking polygon lives
// in static obj 0xA9B47900, which BuildEngineWithCellarFixtures
// intentionally skips today (RegisterStairRampGfxObj is commented
// out). When the cottage GfxObj's full polygon set is added to
// the harness, this assertion will start to fail — flip the test
// to assert the live cn=(0,0,-1) round-trips at that point.
Assert.False(
harnessResult.CollisionNormalValid
&& harnessResult.CollisionNormal.Z < -0.99f,
"Harness should NOT reproduce the cottage-floor cap yet — " +
"if it does, the cottage GfxObj has been added and this test " +
"needs to flip to AssertCallMatchesCapture(engine, captured).");
} }
/// <summary> /// <summary>