diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs
index 6227452..e148d74 100644
--- a/src/AcDream.App/Rendering/GameWindow.cs
+++ b/src/AcDream.App/Rendering/GameWindow.cs
@@ -6100,6 +6100,17 @@ public sealed class GameWindow : IDisposable
}
}
+ // Issue #101 (2026-05-25): retail-faithful phantom check for
+ // GfxObj-only entity sources. Retail's CPartArray::InitParts
+ // emits NO collision shapes when the source GfxObj has
+ // HasPhysics=False / null PhysicsBSP.Root. Our mesh-aabb-fallback
+ // below previously synthesized a clamped [0.30, 1.50]m cylinder
+ // from the visual mesh AABB — that's the source of the 10
+ // phantom 0.80m cyls that block the Holtburg upper-floor
+ // staircase (issue #101). Suppress synthesis for these.
+ // Spec: docs/research/2026-05-25-a6-stairs-cyl-retail-investigation.md.
+ bool isPhantomGfxObj = _physicsDataCache.IsPhantomGfxObjSource(entity.SourceGfxObjOrSetupId);
+
// VISUAL mesh-bounds collision: for SCENERY entities (IDs with
// 0x80000000 bit set, indicating procedurally-placed scenery),
// ALWAYS compute a cylinder from the world-space mesh AABB.
@@ -6114,6 +6125,7 @@ public sealed class GameWindow : IDisposable
// L-fix3: skip entirely when the Setup is phantom — retail
// decorative meshes have no collision data on purpose.
if (!isPhantomSetup
+ && !isPhantomGfxObj
&& !_isLandblockStab
&& (_isOutdoorMesh || (entityBsp == 0 && entityCyl == 0))
&& entity.MeshRefs.Count > 0)
diff --git a/src/AcDream.Core/Physics/PhysicsDataCache.cs b/src/AcDream.Core/Physics/PhysicsDataCache.cs
index fbe8a2d..c7b65a5 100644
--- a/src/AcDream.Core/Physics/PhysicsDataCache.cs
+++ b/src/AcDream.Core/Physics/PhysicsDataCache.cs
@@ -370,7 +370,7 @@ public sealed class PhysicsDataCache
/// short-circuited at the early-return on line 45/46. Retail's
/// CPartArray::InitParts emits NO collision shapes for
/// these — acdream's mesh-aabb-fallback synthesis at
- /// GameWindow.cs:6116 must do the same.
+ /// GameWindow.cs:6127 must do the same.
///
public bool IsPhantomGfxObjSource(uint sourceId)
{