acdream/docs/superpowers/plans/2026-06-03-p0-conformance-apparatus.md
Erik a90f34368f test(p0): dat-backed conformance loader + characterized cottage-doorway topology
P0 (verbatim-spatial-pipeline-port) Tasks 1+2. ConformanceDats loads the
cottage-doorway cells from the real dats with their real ContainmentBsp;
CottageDoorwayCharacterizationTests maps the Holtburg 0140..017F indoor
neighborhood and pins the master-plan threshold building (origin
161.93,7.50,94.00): 0xA9B40170 vestibule (exit portal 0xFFFF + portal to
0171), 0xA9B40171 room. Grid math confirms the outdoor side is landcell
0xA9B40031 -> the 0031<->0170<->0171 ping-pong is verified real. Verified
interior points recorded for the point_in_cell/find_cell_list goldens.

Plan: docs/superpowers/plans/2026-06-03-p0-conformance-apparatus.md
Notes: docs/research/2026-06-03-p0-conformance-apparatus-notes.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 14:20:17 +02:00

709 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# P0 — Conformance Apparatus Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans (inline) to
> implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Build the headless, dat-backed conformance apparatus that lets every later phase
(P1 membership, P2 door collision, P3 camera, P4 PView render) prove a port is **verbatim
retail** — not "vibes" — by asserting real-retail outcomes (`point_in_cell`, `find_cell_list`,
PVS visible-set) against fixtures loaded from the real client dats and against captured retail
cdb traces.
**Architecture:** Three layers. (1) A dat-backed **fixture loader** that hydrates the Holtburg
cottage-doorway cell neighborhood from the real dats with their *real* containment BSPs (no
synthetic BSP, no JSON round-trip). (2) **Golden conformance tests** that pin retail-faithful
outcomes — `point_in_cell` is geometric ground truth (the BSP *is* retail data, loaded from the
same dats retail loads); `find_cell_list` pins the membership pick, with the subtle
doorway-threshold case backed by a captured **retail cdb trace**. (3) **cdb value-capture
tooling** — a new script that dumps `find_cell_list`'s cell-id argument/return at the threshold
(value, not just hit-count), plus a parser that turns its log into a golden fixture. The live
capture run is the single user-gated step; everything else is autonomous + headless.
**Tech Stack:** C# / .NET 10, xUnit, `DatReaderWriter` (`DatCollection.Get<T>`), the existing
`PhysicsDataCache` / `CellTransit` / `BSPQuery` engine, cdb (`acclient.pdb`) per the CLAUDE.md
retail-debugger toolchain.
**Scope note — what P0 is NOT.** P0 does not change any production membership/collision/render
code. It only adds `tests/` + `tools/cdb/` + `docs/`. The PVS visible-set golden is
*scaffolded* (structure + retail anchor + a skipped placeholder) but not filled — a retail
`cell_draw_list` trace is a P3/P4-coupled capture and the PVS code itself is replaced in P4.
P0's load-bearing deliverable is the **membership** goldens (`point_in_cell` + `find_cell_list`)
that P1 consumes immediately, plus ≥1 assertion backed by a real retail trace (the P1 gate).
---
## File Structure
| File | Responsibility |
|---|---|
| `tests/AcDream.Core.Tests/Conformance/ConformanceDats.cs` (create) | Shared dat-dir resolution + one-call headless load of an `EnvCell` (real `ContainmentBsp`) and a cached `CellPhysics` by cell id. The single seam tests use to reach the real dats. |
| `tests/AcDream.Core.Tests/Conformance/CottageDoorwayCharacterizationTests.cs` (create) | Characterize-and-pin the real cottage-doorway cell topology (ids, worldOrigin, portals incl. the `0xFFFF` exit portal, `seenOutside`, has-ContainmentBsp). Discovers the exact IDs the rest of P0 uses. |
| `tests/AcDream.Core.Tests/Conformance/PointInCellConformanceTests.cs` (create) | Golden `point_in_cell`: assert containment for geometrically-known points against the real BSP. Retail-faithful by construction. |
| `tests/AcDream.Core.Tests/Conformance/FindCellListConformanceTests.cs` (create) | Golden `find_cell_list`: unambiguous picks (clearly-inside / clearly-outside) from dats + the doorway-threshold pick pinned to a captured retail trace. |
| `tests/AcDream.Core.Tests/Conformance/RetailTrace.cs` (create) | Parser: read a `find-cell-list` retail cdb log → strongly-typed `RetailCellPick[]` golden records. TDD'd against a checked-in sample log line. |
| `tools/cdb/find-cell-list-capture.cdb` (create) | New cdb script: breakpoint `CObjCell::find_cell_list`, dump the input position + the returned containing cell id (value capture). Ready for the user to run against live retail at the cottage doorway. |
| `tools/cdb/README-find-cell-list-capture.md` (create) | Operator runbook for the capture: prerequisites, exact launch command, what to walk, where the log lands, how to fold it into the golden. |
| `docs/research/2026-06-03-p0-conformance-apparatus-notes.md` (create) | Living notes: the characterized cottage-doorway topology, the golden values + their provenance (geometric vs retail-trace), and the P1-entry checklist. |
---
## Task 1 — Shared dat-backed fixture loader
**Files:**
- Create: `tests/AcDream.Core.Tests/Conformance/ConformanceDats.cs`
The loader mirrors the proven dat-read pattern in
`tests/AcDream.Core.Tests/Physics/DoorBugTrajectoryReplayTests.cs:184-219`
(`DatCollection.Get<EnvCell>``Get<Environment>``CellStruct` → physics-verbatim
`worldTransform`) and the `EnvCell.FromDat` derivation
(`src/AcDream.Core/World/Cells/EnvCell.cs:42-76`). It returns BOTH a `EnvCell` (for
`PointInCell`) and a cached `CellPhysics` (for `CellTransit`), so membership tests have one seam.
- [ ] **Step 1: Write the loader**
```csharp
using System;
using System.IO;
using System.Numerics;
using AcDream.Core.Physics;
using AcDream.Core.World.Cells;
using DatReaderWriter;
using DatReaderWriter.Options;
using DatEnvCell = DatReaderWriter.DBObjs.EnvCell;
using DatEnvironment = DatReaderWriter.DBObjs.Environment;
using Env = System.Environment;
namespace AcDream.Core.Tests.Conformance;
/// <summary>
/// P0 conformance apparatus — headless load of the real Holtburg dats.
/// Tests that need real cell geometry (the retail containment BSP) resolve
/// the dat dir here and load cells via <see cref="LoadEnvCell"/>. Returns
/// null dat dir when the dats are absent (CI) so callers can skip cleanly,
/// matching DoorBugTrajectoryReplayTests.ResolveDatDir.
/// </summary>
public static class ConformanceDats
{
private const uint EnvironmentFilePrefix = 0x0D000000u; // dat namespace for Environment files
/// <summary>The Holtburg landblock these fixtures live in.</summary>
public const uint HoltburgLandblock = 0xA9B40000u;
/// <summary>Resolve the client dat directory, or null if unavailable (skip the test).</summary>
public static string? ResolveDatDir()
{
var fromEnv = Env.GetEnvironmentVariable("ACDREAM_DAT_DIR");
if (!string.IsNullOrWhiteSpace(fromEnv) && Directory.Exists(fromEnv))
return fromEnv;
var def = Path.Combine(
Env.GetFolderPath(Env.SpecialFolder.UserProfile),
"Documents", "Asheron's Call");
return Directory.Exists(def) ? def : null;
}
/// <summary>The physics-verbatim cell→world transform (no +2cm render lift).</summary>
public static Matrix4x4 WorldTransform(DatEnvCell datCell) =>
Matrix4x4.CreateFromQuaternion(datCell.Position.Orientation) *
Matrix4x4.CreateTranslation(datCell.Position.Origin);
/// <summary>
/// Load one EnvCell from the dats with its REAL containment BSP, and register
/// it into <paramref name="cache"/> as a CellPhysics. Returns the high-level
/// EnvCell (PointInCell) so a single load serves both membership predicates.
/// </summary>
public static EnvCell LoadEnvCell(DatCollection dats, PhysicsDataCache cache, uint cellId)
{
var datCell = dats.Get<DatEnvCell>(cellId)
?? throw new InvalidOperationException($"EnvCell 0x{cellId:X8} not found in dats");
var environment = dats.Get<DatEnvironment>(EnvironmentFilePrefix | datCell.EnvironmentId)
?? throw new InvalidOperationException($"Environment 0x{datCell.EnvironmentId:X8} not found");
if (!environment.Cells.TryGetValue(datCell.CellStructure, out var cellStruct) || cellStruct is null)
throw new InvalidOperationException($"CellStruct {datCell.CellStructure} missing from environment");
var world = WorldTransform(datCell);
cache.CacheCellStruct(cellId, datCell, cellStruct, world); // physics CellPhysics (real CellBSP)
return EnvCell.FromDat(cellId, datCell, cellStruct, world); // render/containment EnvCell (real ContainmentBsp)
}
}
```
- [ ] **Step 2: Verify it compiles**
Run: `dotnet build tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj -c Debug`
Expected: build succeeds (no test yet — Task 2 exercises it).
- [ ] **Step 3: Commit**
```bash
git add tests/AcDream.Core.Tests/Conformance/ConformanceDats.cs
git commit -m "test(p0): dat-backed cottage-doorway fixture loader (ConformanceDats)"
```
---
## Task 2 — Characterize and pin the cottage-doorway topology
**Files:**
- Create: `tests/AcDream.Core.Tests/Conformance/CottageDoorwayCharacterizationTests.cs`
- Create: `docs/research/2026-06-03-p0-conformance-apparatus-notes.md`
The master plan names the neighborhood loosely (`0xA9B4003x` + `0xA9B4017x`), and the `0170`/`0171`
ids are reused across buildings in landblock `0xA9B4`. So this task **discovers** the real
topology and pins it. The candidate indoor ids are `0xA9B40170` and `0xA9B40171` (the
ping-pong pair named in the master plan §0). The test prints each cell's structure on first run;
fill the asserts from the printout, then they become the pinned characterization.
- [ ] **Step 1: Write the characterization test (observe form)**
```csharp
using System;
using System.Linq;
using AcDream.Core.Physics;
using DatReaderWriter;
using DatReaderWriter.Options;
using Xunit;
using Xunit.Abstractions;
namespace AcDream.Core.Tests.Conformance;
public class CottageDoorwayCharacterizationTests
{
private readonly ITestOutputHelper _out;
public CottageDoorwayCharacterizationTests(ITestOutputHelper output) => _out = output;
// Candidate indoor cells of the cottage-doorway ping-pong (master plan §0: 0031↔0170↔0171).
public static readonly uint[] CandidateIndoor = { 0xA9B40170u, 0xA9B40171u };
[Fact]
public void Characterize_CottageDoorwayCells_PrintStructure()
{
var datDir = ConformanceDats.ResolveDatDir();
if (datDir is null) { _out.WriteLine("SKIP: dats unavailable"); return; }
using var dats = new DatCollection(datDir, DatAccessType.Read);
var cache = new PhysicsDataCache();
foreach (var id in CandidateIndoor)
{
var cell = ConformanceDats.LoadEnvCell(dats, cache, id);
var phys = cache.GetCellStruct(id)!;
var origin = System.Numerics.Vector3.Transform(
System.Numerics.Vector3.Zero, phys.WorldTransform);
bool hasExitPortal = phys.Portals!.Any(p => p.OtherCellId == 0xFFFFu);
_out.WriteLine(
$"0x{id:X8}: worldOrigin=({origin.X:F2},{origin.Y:F2},{origin.Z:F2}) " +
$"seenOutside={cell.SeenOutside} hasContainmentBsp={cell.ContainmentBsp?.Root is not null} " +
$"portals={phys.Portals!.Count} exitPortal={hasExitPortal} " +
$"stab={cell.StabList.Count} " +
$"portalDests=[{string.Join(",", phys.Portals!.Select(p => $"0x{p.OtherCellId:X4}"))}]");
}
}
}
```
- [ ] **Step 2: Run it and READ the printed structure**
Run: `dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj -c Debug --filter "FullyQualifiedName~Characterize_CottageDoorwayCells" -l "console;verbosity=detailed"`
Expected: PASS, with two `0xA9B401XX: worldOrigin=... portalDests=[...]` lines in the output.
**Record those lines** in `docs/research/2026-06-03-p0-conformance-apparatus-notes.md` under a
"Characterized topology" heading. They are the source of truth for Tasks 2b/3.
> If a candidate id throws "not found in dats", it is the wrong id — widen the candidate scan to
> `0xA9B40170..0xA9B40179` in the loop, re-run, and identify the two cells whose `worldOrigin`
> matches the cottage (low Y, near the `014x` cellar) and that form a portal pair (one's
> `portalDests` contains the other). Pin those two ids.
- [ ] **Step 3: Pin the discovered values (assert form)**
Replace the print-only body with explicit asserts using the recorded values, e.g. (substitute
the REAL numbers you recorded — these are illustrative):
```csharp
[Fact]
public void CottageDoorway_Cell0170_IsVestibuleWithExitPortal()
{
var datDir = ConformanceDats.ResolveDatDir();
if (datDir is null) return;
using var dats = new DatCollection(datDir, DatAccessType.Read);
var cache = new PhysicsDataCache();
var cell = ConformanceDats.LoadEnvCell(dats, cache, 0xA9B40170u);
var phys = cache.GetCellStruct(0xA9B40170u)!;
Assert.True(cell.ContainmentBsp?.Root is not null, "real cell BSP must load");
Assert.NotEmpty(phys.Portals!);
// Pin whatever the characterization printed — exit portal presence is the load-bearing fact:
Assert.Contains(phys.Portals!, p => p.OtherCellId == 0xFFFFu || p.OtherCellId == 0x0171u);
}
```
Keep the `Characterize_..._PrintStructure` test (it documents the data and re-runs cheaply).
- [ ] **Step 4: Run to verify the pinned asserts pass**
Run: `dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj -c Debug --filter "FullyQualifiedName~CottageDoorwayCharacterizationTests"`
Expected: PASS (both tests).
- [ ] **Step 5: Commit**
```bash
git add tests/AcDream.Core.Tests/Conformance/CottageDoorwayCharacterizationTests.cs docs/research/2026-06-03-p0-conformance-apparatus-notes.md
git commit -m "test(p0): characterize + pin cottage-doorway cell topology from dats"
```
---
## Task 3 — `point_in_cell` golden conformance
**Files:**
- Create: `tests/AcDream.Core.Tests/Conformance/PointInCellConformanceTests.cs`
- Modify: `docs/research/2026-06-03-p0-conformance-apparatus-notes.md`
Retail's `point_in_cell` for an EnvCell *is* `BSPTREE::point_in_cell_bsp` against `cell_bsp`
(master plan A6; reference doc §6.3). Our `EnvCell.PointInCell``BSPQuery.PointInsideCellBsp`
is the port. The BSP is loaded from the *same dats retail loads*, so a geometrically-correct
containment answer **is** the retail answer. Goldens: a point at the cell's world origin-ish
interior must be inside; a point far outside the landblock must be outside; the matching point
must be inside exactly one of the doorway pair.
- [ ] **Step 1: Write the failing test**
```csharp
using System.Numerics;
using AcDream.Core.Physics;
using DatReaderWriter;
using DatReaderWriter.Options;
using Xunit;
namespace AcDream.Core.Tests.Conformance;
public class PointInCellConformanceTests
{
[Fact]
public void PointInCell_InteriorPoint_IsInsideItsOwnCell()
{
var datDir = ConformanceDats.ResolveDatDir();
if (datDir is null) return;
using var dats = new DatCollection(datDir, DatAccessType.Read);
var cache = new PhysicsDataCache();
var cell = ConformanceDats.LoadEnvCell(dats, cache, 0xA9B40171u);
var phys = cache.GetCellStruct(0xA9B40171u)!;
// A point just above the cell's covering-sphere centre, in world space, is interior.
var localCentre = phys.CellBSP is not null
? cache.GetCellStruct(0xA9B40171u)!.WorldTransform // placeholder; replaced below
: default;
// Use the cell's bounding-sphere origin (cell-local) lifted to world:
var worldCentre = Vector3.Transform(CharacterizedInteriorLocal_0171, phys.WorldTransform);
Assert.True(cell.PointInCell(worldCentre),
$"interior point {worldCentre} must be inside cell 0x0171's BSP");
}
[Fact]
public void PointInCell_FarAwayPoint_IsOutside()
{
var datDir = ConformanceDats.ResolveDatDir();
if (datDir is null) return;
using var dats = new DatCollection(datDir, DatAccessType.Read);
var cache = new PhysicsDataCache();
var cell = ConformanceDats.LoadEnvCell(dats, cache, 0xA9B40171u);
var faraway = new Vector3(10000f, 10000f, 10000f);
Assert.False(cell.PointInCell(faraway), "a point 10 km away cannot be inside");
}
// Pin this from Task 2's characterization: a cell-LOCAL point clearly inside 0x0171's volume
// (e.g. its bounding-sphere origin, or (0,0,1) if the cell is centred on its local origin).
private static readonly Vector3 CharacterizedInteriorLocal_0171 = new(0f, 0f, 1.0f);
}
```
- [ ] **Step 2: Run to verify it fails (or reveals the right interior point)**
Run: `dotnet test ... --filter "FullyQualifiedName~PointInCellConformanceTests" -l "console;verbosity=detailed"`
Expected: `PointInCell_FarAwayPoint_IsOutside` PASSES immediately. `..._IsInsideItsOwnCell` may
FAIL if `CharacterizedInteriorLocal_0171` isn't actually inside — that's the signal to use the
real interior point from Task 2 (the cell's bounding-sphere origin, printed by adding it to the
characterization output).
- [ ] **Step 3: Fix the interior point from characterization data**
Augment the Task-2 print to also emit the cell's bounding-sphere origin (cell-local), then set
`CharacterizedInteriorLocal_0171` to a point you've confirmed is inside (the sphere origin, or a
point nudged toward the floor). Remove the dead `localCentre` placeholder line.
- [ ] **Step 4: Run to verify both pass**
Run: `dotnet test ... --filter "FullyQualifiedName~PointInCellConformanceTests"`
Expected: PASS (both).
- [ ] **Step 5: Commit**
```bash
git add tests/AcDream.Core.Tests/Conformance/PointInCellConformanceTests.cs docs/research/2026-06-03-p0-conformance-apparatus-notes.md
git commit -m "test(p0): point_in_cell golden conformance vs real dat BSP"
```
---
## Task 4 — `find_cell_list` golden conformance (unambiguous cases)
**Files:**
- Create: `tests/AcDream.Core.Tests/Conformance/FindCellListConformanceTests.cs`
`CellTransit.FindCellList(cache, sphereCentre, radius, currentCellId)` is the membership pick.
Unambiguous goldens come straight from the real dats: a sphere clearly inside room `0x0171`
returns `0x0171`; a sphere clearly inside vestibule `0x0170` returns `0x0170`. These pin the pick
without needing a retail trace (the geometry is retail data). The *threshold* case (ping-pong)
is Task 6, backed by a retail trace.
- [ ] **Step 1: Write the failing test**
```csharp
using System.Collections.Generic;
using System.Numerics;
using AcDream.Core.Physics;
using DatReaderWriter;
using DatReaderWriter.Options;
using Xunit;
namespace AcDream.Core.Tests.Conformance;
public class FindCellListConformanceTests
{
private const float FootRadius = 0.4f; // retail player foot-sphere radius (PhysicsBody default)
[Fact]
public void FindCellList_DeepInsideRoom0171_Returns0171()
{
var datDir = ConformanceDats.ResolveDatDir();
if (datDir is null) return;
using var dats = new DatCollection(datDir, DatAccessType.Read);
var cache = new PhysicsDataCache();
ConformanceDats.LoadEnvCell(dats, cache, 0xA9B40170u);
var room = ConformanceDats.LoadEnvCell(dats, cache, 0xA9B40171u);
var phys = cache.GetCellStruct(0xA9B40171u)!;
// A world point confirmed interior to 0x0171 in Task 3:
var inside0171 = Vector3.Transform(DeepInsideLocal_0171, phys.WorldTransform);
Assert.True(room.PointInCell(inside0171), "fixture sanity: point must be inside 0x0171");
// find_cell_list seeded from the room itself must return the room.
uint picked = CellTransit.FindCellList(cache, inside0171, FootRadius, 0xA9B40171u);
Assert.Equal(0xA9B40171u, picked);
}
// Pin from Task 3 (a cell-local point well inside 0x0171, away from any portal plane):
private static readonly Vector3 DeepInsideLocal_0171 = new(0f, 0f, 1.0f);
}
```
- [ ] **Step 2: Run to verify**
Run: `dotnet test ... --filter "FullyQualifiedName~FindCellListConformanceTests" -l "console;verbosity=detailed"`
Expected: PASS. If it returns a different cell, record the actual return in the notes doc and
investigate whether the seed point is too near a portal plane (move it deeper). A genuine
divergence here is itself a finding — document it; do NOT change production code in P0.
- [ ] **Step 3: Commit**
```bash
git add tests/AcDream.Core.Tests/Conformance/FindCellListConformanceTests.cs
git commit -m "test(p0): find_cell_list golden — unambiguous interior picks from dats"
```
---
## Task 5 — Retail-trace parser + cdb value-capture tooling
**Files:**
- Create: `tests/AcDream.Core.Tests/Conformance/RetailTrace.cs`
- Create: `tools/cdb/find-cell-list-capture.cdb`
- Create: `tools/cdb/README-find-cell-list-capture.md`
The autonomous half of the retail-trace golden: the **parser** (TDD'd against a sample line) and
the **capture script** (ready to run). The capture *run* is the user gate in Task 6.
The cdb script breakpoints `CObjCell::find_cell_list` (`acclient!CObjCell::find_cell_list`,
`0x52b4e0`). `this` (the seed cell, `thiscall``ecx`) carries `objcell_id` at a known offset;
the position is an argument. We log the seed cell id and the position, and (via a return
breakpoint) the picked cell id. The exact field offsets are confirmed at capture time with
`dt acclient!CObjCell @ecx` (CLAUDE.md retail-debugger watchouts). The script emits lines of the
form `[fcl] seed=0xHHHHHHHH px=<f> py=<f> pz=<f> picked=0xHHHHHHHH`.
- [ ] **Step 1: Write the failing parser test**
```csharp
using System.Numerics;
using Xunit;
namespace AcDream.Core.Tests.Conformance;
public class RetailTraceTests
{
[Fact]
public void Parse_FindCellListLine_YieldsSeedPosAndPicked()
{
const string line =
"[fcl] seed=0xA9B40170 px=141.5000 py=7.2200 pz=92.7400 picked=0xA9B40171";
var rec = RetailTrace.ParseFindCellList(line);
Assert.NotNull(rec);
Assert.Equal(0xA9B40170u, rec!.SeedCellId);
Assert.Equal(new Vector3(141.5f, 7.22f, 92.74f), rec.Position);
Assert.Equal(0xA9B40171u, rec.PickedCellId);
}
[Fact]
public void Parse_NonMatchingLine_ReturnsNull()
{
Assert.Null(RetailTrace.ParseFindCellList("[BP4] find_collisions hit#10170 collide=0"));
}
}
```
- [ ] **Step 2: Run to verify it fails**
Run: `dotnet test ... --filter "FullyQualifiedName~RetailTraceTests"`
Expected: FAIL — `RetailTrace` does not exist.
- [ ] **Step 3: Write the parser**
```csharp
using System;
using System.Globalization;
using System.Numerics;
using System.Text.RegularExpressions;
namespace AcDream.Core.Tests.Conformance;
/// <summary>A single retail find_cell_list pick captured via cdb (golden oracle).</summary>
public sealed record RetailCellPick(uint SeedCellId, Vector3 Position, uint PickedCellId);
/// <summary>Parser for the find-cell-list-capture.cdb log format.</summary>
public static class RetailTrace
{
private static readonly Regex Fcl = new(
@"^\[fcl\]\s+seed=0x(?<seed>[0-9A-Fa-f]{1,8})\s+" +
@"px=(?<px>-?\d+(\.\d+)?)\s+py=(?<py>-?\d+(\.\d+)?)\s+pz=(?<pz>-?\d+(\.\d+)?)\s+" +
@"picked=0x(?<picked>[0-9A-Fa-f]{1,8})\s*$",
RegexOptions.Compiled);
public static RetailCellPick? ParseFindCellList(string line)
{
var m = Fcl.Match(line);
if (!m.Success) return null;
var ci = CultureInfo.InvariantCulture;
return new RetailCellPick(
SeedCellId: Convert.ToUInt32(m.Groups["seed"].Value, 16),
Position: new Vector3(
float.Parse(m.Groups["px"].Value, ci),
float.Parse(m.Groups["py"].Value, ci),
float.Parse(m.Groups["pz"].Value, ci)),
PickedCellId: Convert.ToUInt32(m.Groups["picked"].Value, 16));
}
}
```
- [ ] **Step 4: Run to verify it passes**
Run: `dotnet test ... --filter "FullyQualifiedName~RetailTraceTests"`
Expected: PASS (both).
- [ ] **Step 5: Write the cdb capture script**
```
$$ find-cell-list-capture.cdb — value-capture of CObjCell::find_cell_list at the cottage doorway.
$$ Logs the seed cell id + input position + picked cell id (NOT just a hit count).
$$ Prereqs: retail in-world at the Holtburg cottage doorway; PDB matches (check_exe_pdb.py = MATCH).
$$ Offsets for objcell_id + the position arg are CONFIRMED at runtime with `dt acclient!CObjCell @ecx`
$$ and `dv` — edit the @ecx+OFFSET below if the dt dump shows a different layout.
.logopen C:\Users\erikn\source\repos\acdream\find-cell-list-capture.log
.sympath C:\Users\erikn\source\repos\acdream\refs
.symopt+ 0x40
.reload /f acclient.exe
r $t0 = 0
$$ Entry: dump seed cell id (this->objcell_id @ ecx+0x20 — VERIFY with dt) + the position arg.
bp acclient!CObjCell::find_cell_list "r $t0 = @$t0 + 1; .printf /D \"[fcl-entry#%d] seed=0x%08x\\n\", @$t0, poi(@ecx+0x20); .if (@$t0 >= 4000) { qd } .else { gc }"
.printf \"find_cell_list capture armed. Walk SLOWLY in/out of the cottage doorway now.\\n\"
g
```
- [ ] **Step 6: Write the operator runbook**
Create `tools/cdb/README-find-cell-list-capture.md` with: the `check_exe_pdb.py` MATCH
precondition; the exact PowerShell launch (`cdb.exe -pn acclient.exe -cf find-cell-list-capture.cdb`);
the instruction to first run `dt acclient!CObjCell @ecx` once to confirm the `objcell_id` offset
and `dv` to find the position arg, then edit the script; the walk to perform (stand in the
doorway, step in and out across the threshold 510×); where the log lands; and how to hand the
log to Task 6.
- [ ] **Step 7: Commit**
```bash
git add tests/AcDream.Core.Tests/Conformance/RetailTrace.cs tools/cdb/find-cell-list-capture.cdb tools/cdb/README-find-cell-list-capture.md
git commit -m "test(p0): retail find_cell_list trace parser + value-capture cdb script"
```
---
## Task 6 — The retail-trace golden (USER GATE) + P1-entry checklist
**Files:**
- Modify: `tests/AcDream.Core.Tests/Conformance/FindCellListConformanceTests.cs`
- Create: `tests/AcDream.Core.Tests/Conformance/Fixtures/find-cell-list-threshold.log` (from the capture)
- Modify: `docs/research/2026-06-03-p0-conformance-apparatus-notes.md`
This task has the one irreducible user-gated step: a live retail cdb capture. Everything is
prepped (Task 5). After the capture, encode ≥1 retail pick as a golden assertion — satisfying the
kickoff's "do NOT start P1 before ≥1 golden retail-trace assertion exists."
- [ ] **Step 1: USER GATE — capture the retail trace**
Mining the existing committed traces first (`docs/research/2026-05-21-a6-captures/scen1_inn_doorway/retail.decoded.log`
and `scen4_cottage_cellar/retail.decoded.log`): grep for any line carrying a `find_cell_list`
cell-id pick. If one exists in usable form, use it (no new capture needed). Otherwise, ask the
user to run `tools/cdb/find-cell-list-capture.cdb` per its README against live retail at the
cottage doorway, and place the resulting log at
`tests/AcDream.Core.Tests/Conformance/Fixtures/find-cell-list-threshold.log`.
- [ ] **Step 2: Add the trace-backed golden**
For each captured `RetailCellPick`, assert acdream's `CellTransit.FindCellList` returns the same
picked cell for the same `(SeedCellId, Position)`:
```csharp
[Fact]
public void FindCellList_DoorwayThreshold_MatchesRetailTrace()
{
var datDir = ConformanceDats.ResolveDatDir();
if (datDir is null) return;
var fixturePath = System.IO.Path.Combine(
System.AppContext.BaseDirectory, "Conformance", "Fixtures", "find-cell-list-threshold.log");
if (!System.IO.File.Exists(fixturePath)) return; // gate not yet satisfied — skip until captured
using var dats = new DatCollection(datDir, DatAccessType.Read);
var cache = new PhysicsDataCache();
foreach (var id in new[] { 0xA9B40170u, 0xA9B40171u })
ConformanceDats.LoadEnvCell(dats, cache, id);
foreach (var raw in System.IO.File.ReadAllLines(fixturePath))
{
var pick = RetailTrace.ParseFindCellList(raw);
if (pick is null) continue;
uint ours = CellTransit.FindCellList(cache, pick.Position, 0.4f, pick.SeedCellId);
Assert.Equal(pick.PickedCellId, ours);
}
}
```
> Per master-plan no-shortcuts rule §4: if this assertion fails, it means *acdream diverges from
> retail* at the threshold — that is the P1 work, captured as a RED conformance test. Leave it RED
> (documents-the-bug) and let P1 turn it GREEN. Do NOT weaken the assertion.
- [ ] **Step 3: Wire the fixture into the test project so it copies to output**
Modify `tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj` to copy `Conformance/Fixtures/**`
to the output dir (mirror how existing `Fixtures/issue98/**` is copied — find that `<ItemGroup>`
and add the `Conformance/Fixtures` glob, or confirm a wildcard already covers it).
- [ ] **Step 4: Run the full conformance suite**
Run: `dotnet test tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj -c Debug --filter "FullyQualifiedName~Conformance"`
Expected: all GREEN, except possibly the documents-the-bug threshold test if acdream diverges
(that RED is a finding for P1, not a P0 failure).
- [ ] **Step 5: Write the P1-entry checklist into the notes doc**
In `docs/research/2026-06-03-p0-conformance-apparatus-notes.md`, record: the characterized
topology; every golden + its provenance (geometric vs retail-trace); whether the threshold golden
is GREEN (acdream already matches) or RED (P1 must fix); and the explicit statement "P0 gate met:
≥1 retail-trace-backed assertion exists" so P1 can begin.
- [ ] **Step 6: Commit**
```bash
git add tests/AcDream.Core.Tests/Conformance/ docs/research/2026-06-03-p0-conformance-apparatus-notes.md tests/AcDream.Core.Tests/AcDream.Core.Tests.csproj
git commit -m "test(p0): retail-trace-backed find_cell_list threshold golden (P1 gate met)"
```
---
## Task 7 — PVS-golden scaffold (structure only, fill deferred to P3/P4)
**Files:**
- Create: `tests/AcDream.Core.Tests/Conformance/PvsConformanceTests.cs`
The master plan lists "PVS visible-set for a given (cell, eye) matches" under P0, but a retail
`cell_draw_list` trace is a P3/P4-coupled capture and the PVS code is replaced in P4. So P0
scaffolds the test (intent + retail anchor + a skipped placeholder) so the structure exists when
P4 captures the retail visible-set.
- [ ] **Step 1: Write the scaffold (skipped)**
```csharp
using Xunit;
namespace AcDream.Core.Tests.Conformance;
/// <summary>
/// PVS (portal-visibility) conformance — scaffold. Retail oracle:
/// PView::ConstructView @ 0x005a57b0 (pc:433750) produces cell_draw_list; the golden is a
/// captured retail cell_draw_list for a given (viewer_cell, eye). Filled in P3/P4 when the
/// camera viewer-cell + ConstructView ports land and a retail cell_draw_list trace is captured
/// (new cdb script breakpointing PView::DrawCells / cell_draw_list, sibling to
/// find-cell-list-capture.cdb). See docs/research/2026-06-02-retail-render-pipeline-full-reference.md §3.
/// </summary>
public class PvsConformanceTests
{
[Fact(Skip = "P0 scaffold — filled in P4 with a captured retail cell_draw_list trace")]
public void Pvs_CottageInterior_MatchesRetailCellDrawList() { }
}
```
- [ ] **Step 2: Verify it builds + reports as skipped**
Run: `dotnet test ... --filter "FullyQualifiedName~PvsConformanceTests"`
Expected: 1 skipped, 0 failed.
- [ ] **Step 3: Commit**
```bash
git add tests/AcDream.Core.Tests/Conformance/PvsConformanceTests.cs
git commit -m "test(p0): PVS-golden conformance scaffold (filled in P4)"
```
---
## Self-Review
**Spec coverage (master-plan P0 + reference §G4):**
- "Headless fixtures of the cottage neighborhood loaded from real dats" → Task 1 (`ConformanceDats.LoadEnvCell`) + Task 2 (characterize).
- "`point_in_cell` matches" → Task 3 (geometric goldens vs real BSP).
- "`find_cell_list` returns the same cell as a captured retail trace at the threshold" → Task 4 (unambiguous) + Task 6 (threshold, retail-trace-backed).
- "the PVS visible-set ... matches" → Task 7 (scaffold; fill in P4, documented).
- "Use the existing `ACDREAM_CAPTURE_RESOLVE` + cdb retail traces" → Task 5 (parser + capture script) + Task 6 (mine existing traces first, else live capture).
- "≥1 golden retail-trace assertion before P1" → Task 6 (the P1 gate).
**Placeholder scan:** Interior-point constants (`*_Local_0171`) and pinned cell asserts are
explicitly characterization-derived (Task 2/3 print-then-pin) — concrete process, not hand-waving.
The cdb field offset (`ecx+0x20`) is flagged as runtime-verified (`dt acclient!CObjCell @ecx`).
**Type consistency:** `ConformanceDats.LoadEnvCell` returns `EnvCell`; `cache.GetCellStruct(id)`
returns `CellPhysics` (matches `DoorBugTrajectoryReplayTests`); `CellTransit.FindCellList(cache,
Vector3, float, uint) → uint` (matches the membership map); `RetailTrace.ParseFindCellList(string)
→ RetailCellPick?`. Consistent across Tasks 1/3/4/6.
**Risks / open verifications (resolve during execution, not blocking):**
- `PhysicsDataCache.CacheCellStruct` + `GetCellStruct` + `CellPhysics.WorldTransform/Portals/CellBSP`
member names are from the door-test read at `DoorBugTrajectoryReplayTests.cs:213-225`; verify
exact casing on first compile.
- The two cottage-doorway ids may not be `0170`/`0171` — Task 2 discovers the truth and the
later tasks consume whatever Task 2 pins.
- Whether the existing committed retail traces already contain a usable `find_cell_list` pick is
checked first in Task 6 Step 1 before asking the user for a live capture.