fix(phys): #112 - remove the non-retail escape-hatch demote from the pick; lateral stab-graph recovery + retail keep-curr

Root cause (oracle: CLandCell::point_in_cell :316941 = terrain-poly only;
find_cell_list null-result keep-curr pc:308788-308825; CEnvCell::
check_building_transit :309827 = sphere_intersects_cell per portal-adjacent
cell): retail KEEPS curr_cell when nothing contains the centre — including
inside a house's containment gaps. Our 6dbbf95 escape hatch instead demoted
any hydrated indoor claim the sphere no longer overlaps to the outdoor
column; at the A9B3 hill cottage's real interior gap this stranded the
player outdoor-classified deep indoors, where re-promotion is portal-
adjacent-only (retail-identical) -> the outdoor flood rendered the interior
transparent (the user's "sometimes transparent" walk).

The hatch's actual target - poisoned (cell, position) SAVES - has been
handled at the SNAP by PhysicsEngine.Resolve's AdjustPosition validation
since #107/#111, so the per-tick pick reverts to retail semantics:
1. lateral recovery first - when the sphere no longer overlaps the claim,
   search the claim's stab list for a containing cell (retail
   find_visible_child_cell :311444, the same recovery AdjustPosition uses);
   the #111 adjacent-claim shape now self-heals laterally (dat-backed test:
   pick(seed 0x172) at a 0x171-interior point -> 0x171);
2. else KEEP curr_cell (retail null-result).

Two old tests asserting the hatch demote rewritten to the retail semantics
(tests-can-codify-bugs); P1 retail-golden conformance gates explicitly green
(FindCellListConformance + ThresholdPortalCrossing + CottageDoorway +
CameraCornerSeal = 11/11). New Issue112MembershipTests: the lateral-recovery
fact + a DocumentsResidual fact pinning the remaining at-doorway gap demote
(via the NORMAL outdoor-candidate path; open oracle read = retail's
add_all_outside_cells gate in CEnvCell::find_transit_cells pc:317499 -
sphere-proximity vs graph-reachability). Core 1383 + 4 pre-existing #99
failures + 1 skip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-10 15:03:49 +02:00
parent e9c8a925d2
commit 2d6954ee44
4 changed files with 133 additions and 35 deletions

View file

@ -265,15 +265,19 @@ public class CellTransitFindCellSetTests
}
[Fact]
public void IndoorSeed_SphereFullyOutsideHydratedCell_DemotesToOutdoorColumn()
public void IndoorSeed_SphereFullyOutsideHydratedCell_KeepsCurrent_RetailNullResult()
{
// The gate-2 live wedge shape: claimed cell 0xA9B40150, sphere far
// outside its volume (x = -10, fully behind the x≥0 half-space).
// The BFS finds no portals (sphere nowhere near them), so no outdoor
// candidates exist — pre-fix this returned the bogus claim forever.
// Expected: the global outdoor column under the centre. x=-10 is one
// cell WEST of the A9B4 block edge → block 0xA8B4, lcoord (1351,1440)
// → cell (7,0) → low 0x39.
// #112 (2026-06-10): REWRITTEN from the 6dbbf95 escape-hatch
// assertion (was: demote to the outdoor column 0xA8B40039). Retail
// find_cell_list leaves *result null when NOTHING contains the centre
// and the caller KEEPS curr_cell (pc:308788-308825) — the hatch's
// demote was a non-retail addition that also fired on legitimate
// sub-meter containment gaps INSIDE houses (the A9B3 cottage),
// stranding the player outdoor-classified deep indoors → transparent
// interior. The hatch's actual target (poisoned saves) is handled at
// the SNAP by PhysicsEngine.Resolve's AdjustPosition validation
// (#107/#111). With no stab-list on this fixture cell, the lateral
// recovery finds nothing → keep the claim.
var cache = new PhysicsDataCache();
cache.RegisterCellStructForTest(0xA9B40150u, MakeCellWithBoundedBsp(Matrix4x4.Identity));
@ -282,7 +286,7 @@ public class CellTransitFindCellSetTests
currentCellId: 0xA9B40150u,
out _);
Assert.Equal(0xA8B40039u, containing);
Assert.Equal(0xA9B40150u, containing);
}
[Fact]