feat(phys): A6.P3 slice 5 — [place-fail] probe + sharpened #98 diagnosis
Add ACDREAM_PROBE_PLACEMENT_FAIL gate + LogPlacementFail emitter + side-channel polygon attribution in PhysicsDiagnostics. Wire into BSPQuery.FindCollisions Path 1 (Placement/Ethereal) on Collided returns; wire into Transition.DoStepDown after the placement_insert TransitionalInsert(1) call; wire into Transition.FindObjCollisions to emit per-static-object [place-fail-obj] lines. Run scen4 cellar-up with the probe → 168 [place-fail] events. 80 of 81 BSPQuery Path 1 placement rejections cite polygon 0x0020 in cellar cell 0xA9B40147's BSP: n=(0,0,-1) d=-0.2, world Z=93.82 — the cellar ceiling (underside of cottage main floor thickness layer). 0 [place-fail-obj] lines, confirming the failure source is the cell BSP not a static object. The probe-driven evidence INVALIDATES the 2026-05-22 morning handoff's "Path 5 vs Path 6 in BSPQuery.FindCollisions" diagnosis. Retail's BP4 trace shows every find_collisions hit has collide=0 — retail enters the same Contact branch we do, no outer-dispatcher divergence. Retail's BP5 fires 17+ times on the cellar ramp polygon, not "30 hits all on flat planes" as morning claimed. The actual divergence is downstream in cell-promotion: retail's check_cell transitions to cottage cell 0xA9B40146 during the ascent (BP7 sets ContactPlane to the cottage main floor poly, which lives in cottage cell's BSP not cellar's). Ours stays at cellar 0xA9B40147, where the ceiling poly 0x0020 correctly rejects the lifted sphere. No fix attempted this session per CLAUDE.md discipline check (3+ failed fixes = handoff). Full slice 5 evidence + concrete next-session pickup steps at docs/research/2026-05-22-a6-p3-slice5-handoff.md. ISSUES.md #98 updated with the corrected diagnosis. Test baseline: 1148 + 8 pre-existing fail. Maintained. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c479ea68a3
commit
cf3deff7c2
7 changed files with 26489 additions and 9 deletions
|
|
@ -843,13 +843,31 @@ public static class BSPQuery
|
|||
if (node.Type == BSPNodeType.Leaf)
|
||||
{
|
||||
if (node.Polygons.Count == 0) return false;
|
||||
if (centerCheck && node.Solid != 0) return true;
|
||||
if (centerCheck && node.Solid != 0)
|
||||
{
|
||||
// A6.P3 slice 5 (2026-05-22): record the solid-leaf cause for
|
||||
// the [place-fail] probe. Side-channel pattern matches
|
||||
// LastBspHitPoly — gated on the probe flag so the production
|
||||
// path pays only one boolean check.
|
||||
if (PhysicsDiagnostics.ProbePlacementFailEnabled)
|
||||
PhysicsDiagnostics.LastPlacementFailSolidLeaf = true;
|
||||
return true;
|
||||
}
|
||||
if (!NodeIntersects(node, sphere)) return false;
|
||||
|
||||
foreach (ushort polyId in node.Polygons)
|
||||
{
|
||||
if (!resolved.TryGetValue(polyId, out var poly)) continue;
|
||||
if (HitsSphere(poly, sphere)) return true;
|
||||
if (HitsSphere(poly, sphere))
|
||||
{
|
||||
if (PhysicsDiagnostics.ProbePlacementFailEnabled)
|
||||
{
|
||||
PhysicsDiagnostics.LastPlacementFailPolyId = poly.Id;
|
||||
PhysicsDiagnostics.LastPlacementFailPolyNormal = poly.Plane.Normal;
|
||||
PhysicsDiagnostics.LastPlacementFailPolyD = poly.Plane.D;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1647,12 +1665,40 @@ public static class BSPQuery
|
|||
{
|
||||
const bool clearCell = true;
|
||||
|
||||
// A6.P3 slice 5 (2026-05-22) — reset the placement-fail side-channel
|
||||
// before each SphereIntersectsSolidInternal call so a leftover
|
||||
// value from a prior call (or from sphere0 if sphere1 is the actual
|
||||
// failure) doesn't leak into the [place-fail] log.
|
||||
if (PhysicsDiagnostics.ProbePlacementFailEnabled)
|
||||
{
|
||||
PhysicsDiagnostics.LastPlacementFailPolyId = 0;
|
||||
PhysicsDiagnostics.LastPlacementFailSolidLeaf = false;
|
||||
}
|
||||
|
||||
if (SphereIntersectsSolidInternal(root, resolved, sphere0, clearCell))
|
||||
{
|
||||
if (PhysicsDiagnostics.ProbePlacementFailEnabled)
|
||||
PhysicsDiagnostics.LogPlacementFail(
|
||||
"Path1.sphere0", sphere0.Center, sphere0.Radius, 0,
|
||||
path.CheckCellId, worldOrigin, obj.Ethereal);
|
||||
return TransitionState.Collided;
|
||||
}
|
||||
|
||||
if (PhysicsDiagnostics.ProbePlacementFailEnabled)
|
||||
{
|
||||
PhysicsDiagnostics.LastPlacementFailPolyId = 0;
|
||||
PhysicsDiagnostics.LastPlacementFailSolidLeaf = false;
|
||||
}
|
||||
|
||||
if (sphere1 is not null &&
|
||||
SphereIntersectsSolidInternal(root, resolved, sphere1, clearCell))
|
||||
{
|
||||
if (PhysicsDiagnostics.ProbePlacementFailEnabled)
|
||||
PhysicsDiagnostics.LogPlacementFail(
|
||||
"Path1.sphere1", sphere1.Center, sphere1.Radius, 1,
|
||||
path.CheckCellId, worldOrigin, obj.Ethereal);
|
||||
return TransitionState.Collided;
|
||||
}
|
||||
|
||||
return TransitionState.OK;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue