diff --git a/tests/AcDream.Core.Tests/World/Cells/CellGraphFixtureTests.cs b/tests/AcDream.Core.Tests/World/Cells/CellGraphFixtureTests.cs
new file mode 100644
index 0000000..aa78c12
--- /dev/null
+++ b/tests/AcDream.Core.Tests/World/Cells/CellGraphFixtureTests.cs
@@ -0,0 +1,65 @@
+using System;
+using System.IO;
+using System.Numerics;
+using AcDream.Core.Physics;
+using AcDream.Core.World.Cells;
+using Xunit;
+
+namespace AcDream.Core.Tests.World.Cells;
+
+///
+/// UCG Stage 1 grounding test: exercises against a REAL
+/// Holtburg cottage-cellar cell loaded from the #98 fixture set. No production
+/// code is added — this test validates that the existing types work correctly
+/// with real dat-derived geometry.
+///
+/// Vertex-space note (Step 1 finding): vertices
+/// are CELL-LOCAL (sourced from VertexArray.Origin in PhysicsDataCache.ResolvePolygons
+/// without applying WorldTransform). The bounds accumulation below is correct as-is
+/// and the non-degenerate assertions are valid.
+///
+public class CellGraphFixtureTests
+{
+ private const uint CellarCellId = 0xA9B40147u; // #98 cottage cellar fixture
+
+ private static string FixtureDir()
+ {
+ var dir = AppContext.BaseDirectory;
+ while (dir is not null && !File.Exists(Path.Combine(dir, "AcDream.slnx")))
+ dir = Directory.GetParent(dir)?.FullName;
+ Assert.NotNull(dir);
+ return Path.Combine(dir!, "tests", "AcDream.Core.Tests", "Fixtures", "issue98");
+ }
+
+ private static EnvCell EnvFromFixture(uint cellId)
+ {
+ var path = Path.Combine(FixtureDir(), $"0x{cellId:X8}.json");
+ Assert.True(File.Exists(path), $"missing fixture {path}");
+ var cp = CellDumpSerializer.Hydrate(CellDumpSerializer.Read(path));
+
+ // Accumulate cell-local bounds from resolved polygon vertices.
+ // Vertices are cell-local (VertexArray.Origin, no WorldTransform applied).
+ var min = new Vector3(float.MaxValue);
+ var max = new Vector3(float.MinValue);
+ foreach (var poly in cp.Resolved.Values)
+ foreach (var v in poly.Vertices) { min = Vector3.Min(min, v); max = Vector3.Max(max, v); }
+ if (min.X == float.MaxValue) { min = Vector3.Zero; max = Vector3.Zero; }
+
+ return new EnvCell(cellId, cp.WorldTransform, cp.InverseWorldTransform, min, max,
+ Array.Empty(), Array.Empty(), false, cp.CellBSP);
+ }
+
+ [Fact]
+ public void RealCottageCell_ResolvesViaGetVisible_AndIsEnv()
+ {
+ var g = new CellGraph();
+ var env = EnvFromFixture(CellarCellId);
+ g.Add(env);
+
+ var resolved = g.GetVisible(CellarCellId);
+ Assert.Same(env, resolved);
+ Assert.True(resolved!.IsEnv);
+ Assert.True(env.LocalBoundsMax.X > env.LocalBoundsMin.X, "non-degenerate bounds X");
+ Assert.True(env.LocalBoundsMax.Y > env.LocalBoundsMin.Y, "non-degenerate bounds Y");
+ }
+}