# Phase O — Task O-T1 — WB Usage Audit

**Status:** REPORT-ONLY (no source edits, no project changes).
**Filed:** 2026-05-21.
**Purpose:** Closure of every WorldBuilder type and file acdream transitively
uses, so T2–T7 don't miss a dependency at the "drop project references" step.

> **Headline:** Reachable closure from acdream is **33 files / ~7.7 K LOC**
> (~6.8 K from `Chorizite.OpenGLSDLBackend`, ~0.9 K from `WorldBuilder.Shared`).
> Substantially smaller than the spec's §4 component list anticipated, because
> three of the spec's named components (`TerrainRenderManager`,
> `LandSurfaceManager`, `EnvCellRenderManager` / `PortalRenderManager`) are
> **not in our actual call graph** — we already replaced or never used them.
> The 7-8 day estimate in spec §5 looks **reasonable**, possibly conservative.
> O-Q1 (thread-model) verdict: **SAFE** — no worker-thread access to WB code.

Subreports (long-tail per-file tables) by parallel agents:
- `2026-05-21-phase-o-t1-wb-audit-chorizite-closure.md` — Chorizite full per-file table
- `2026-05-21-phase-o-t1-wb-audit-shared-closure.md` — WB.Shared per-file table
- `2026-05-21-phase-o-t1-wb-audit-thread-and-hidden.md` — thread model + hidden deps
- `2026-05-21-phase-o-t1-wb-audit-extensions-and-rest.md` — broad reachability sweep

> Note: those subreport file *names* are referenced in the parent agents'
> output but the Explore agent type lacks `Write`, so the files were not
> persisted to disk. The relevant content has been pulled into the summary
> tables and inventory below.

---

## 1. Direct use surface

Files in `src/` and `tests/` that actually import a WorldBuilder type
(`using WorldBuilder.*` or `using Chorizite.OpenGLSDLBackend*` or fully-
qualified). Comment-only WB references (six files in src/ — TerrainBlending,
SurfaceInfo, GfxObjMesh, SkyRenderer, SamplerCache, GameWindow) are
excluded; they describe ports done by hand and don't carry a project
dependency.

### Production source — `src/`

| File | LOC | WB types used | Notes |
|---|---|---|---|
| [`src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs`](src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs) | 349 | `OpenGLGraphicsDevice`, `DefaultDatReaderWriter`, `ObjectMeshManager`, `ObjectRenderData`, `DebugRenderSettings` | Single seam; constructs WB pipeline; **also constructs `_wbDats = new DefaultDatReaderWriter(datDir)` at line 79** (the second-reader smell Phase O closes) |
| [`src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs`](src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs) | ~1500 | `ObjectRenderData`, `ObjectRenderBatch` | Production draw path (modern MDI); uses our own `DrawElementsIndirectCommand` struct + our own `mesh_modern.{vert,frag}` shaders |
| [`src/AcDream.Core/World/WbSceneryAdapter.cs`](src/AcDream.Core/World/WbSceneryAdapter.cs) | 55 | `TerrainEntry` | Bridges `LandBlock` → `TerrainEntry[81]` for WB scenery helpers |
| [`src/AcDream.Core/World/SceneryGenerator.cs`](src/AcDream.Core/World/SceneryGenerator.cs) | 196 | `SceneryHelpers.Displace/CheckSlope/ObjAlign/RotateObj/ScaleObj`, `TerrainUtils.OnRoad/GetNormal` | Procedural scenery placement; calls WB stateless helpers |
| [`src/AcDream.Core/Textures/SurfaceDecoder.cs`](src/AcDream.Core/Textures/SurfaceDecoder.cs) | 219 | `TextureHelpers.FillIndex16/FillP8/FillA8/FillA8Additive/FillA8R8G8B8/FillR8G8B8/FillR5G6B5/FillA4R4G4B4` | Pure pixel-format decoders |

### Test source — `tests/`

| File | WB types used | Notes |
|---|---|---|
| [`tests/AcDream.Core.Tests/Textures/TextureDecodeConformanceTests.cs`](tests/AcDream.Core.Tests/Textures/TextureDecodeConformanceTests.cs) | `TextureHelpers.Fill*` | Byte-identity tests vs WB; should stay after extraction (validates our copy matches the original) |
| [`tests/AcDream.Core.Tests/Terrain/SplitFormulaDivergenceTest.cs`](tests/AcDream.Core.Tests/Terrain/SplitFormulaDivergenceTest.cs) | `TerrainUtils.CalculateSplitDirection`, `CellSplitDirection` | One-time sweep test that compared WB's formula with retail's; **safely deletable after extraction** (test already informed the N.5b decision — its job is done) |

### Project files

| File | Lines | What |
|---|---|---|
| [`src/AcDream.App/AcDream.App.csproj`](src/AcDream.App/AcDream.App.csproj) | 38-39 | `<ProjectReference>` to `WorldBuilder.Shared` + `Chorizite.OpenGLSDLBackend` |
| [`src/AcDream.Core/AcDream.Core.csproj`](src/AcDream.Core/AcDream.Core.csproj) | 27-28 | same |

These two csproj entries are what T7 deletes after extraction.

---

## 2. Transitive closure (summary)

Closure walked from the direct-use surface above, following only WB
project-internal types. NuGet packages (`Chorizite.Core`,
`Chorizite.DatReaderWriter`, `Silk.NET.*`, `BCnEncoder.Net`,
`SixLabors.ImageSharp`, `MemoryPack`) stay as `<PackageReference>` and are
**not** part of the extraction scope.

| Source project | Files reachable | LOC | Notes |
|---|---|---|---|
| `Chorizite.OpenGLSDLBackend` | **28** | **~6,829** | Entry points: `ObjectMeshManager`, `OpenGLGraphicsDevice`, `TextureHelpers`, `SceneryHelpers`, `ObjectRenderData/Batch`, `DebugRenderSettings` |
| `WorldBuilder.Shared` | **5** | **876** | Entry points: `TerrainEntry`, `TerrainUtils`, `CellSplitDirection`, `DefaultDatReaderWriter`, `IDatReaderWriter` |
| **Total reachable** | **33** | **~7,705** | |

Files in WB **not** in our reachable closure (informational; do NOT
extract): all of `WorldBuilder/`, `WorldBuilder.Server/`, the platform
projects (`WorldBuilder.Windows/Linux/Mac`); inside `WorldBuilder.Shared/`:
`Hubs/` (SignalR), `Migrations/` (EF Core), `Repositories/`, the editor
`Services/` (DocumentManager, SyncService, etc.), `Modules/Landscape/Tools/`
(brush/painting), `Modules/Landscape/Commands/` (undo/redo); inside
`Chorizite.OpenGLSDLBackend/`: `FontRenderer`, `AudioPlaybackEngine`,
`MinimapRenderer`, `BackendGizmoDrawer`, the entire editor scene/camera
subsystems, and **critically: `TerrainRenderManager`, `LandSurfaceManager`,
`EnvCellRenderManager`, `PortalRenderManager`** — see §4 below.

---

## 3. Per-file inventory (top 30 by LOC)

DAT-TOUCH = file references `IDatReaderWriter` / `_dats.Get<T>()` / opens
dat data. GL-TOUCH = file calls Silk.NET.OpenGL directly or writes shader
binds / texture uploads. Bucket per Phase O spec (T3–T6, REPLACE for the
dat-readers we drop, NOT for files in the spec's §4 list that turn out to
not be reachable).

### From `WorldBuilder.Shared/` (5 files, 876 LOC) — ALL EXTRACTED

| Path (rel. `references/WorldBuilder/`) | LOC | Role | DAT? | GL? | Bucket |
|---|---|---|---|---|---|
| `WorldBuilder.Shared/Modules/Landscape/Lib/TerrainUtils.cs` | 258 | Static helpers: `GetHeight`, `GetNormal`, `OnRoad`, `CalculateSplitDirection` | indirect (caller passes Region) | no | **T5** |
| `WorldBuilder.Shared/Modules/Landscape/Models/TerrainEntry.cs` | 248 | Packed per-vertex terrain struct (uses `[MemoryPackable]`) | no | no | **T5** |
| `WorldBuilder.Shared/Services/DefaultDatReaderWriter.cs` | 215 | Concrete dat reader (4 dbs, cache, file handles) | yes | no | **REPLACE** (delete; route through DatCollection) |
| `WorldBuilder.Shared/Services/IDatReaderWriter.cs` | 137 | Dat access interface + `IdResolution` record | yes | no | **REPLACE** (delete) |
| `WorldBuilder.Shared/Modules/Landscape/Models/CellSplitDirection.cs` | 18 | `SWtoNE` / `SEtoNW` enum | no | no | **T5** |

### From `Chorizite.OpenGLSDLBackend/` (28 files, ~6,829 LOC reachable) — TOP 10 by LOC

(Full table in the chorizite-closure subreport; only the top by LOC and a
few key smaller files are inlined here so this doc stays under 500 lines.)

| Path (rel. `references/WorldBuilder/`) | LOC | Role | DAT? | GL? | Bucket |
|---|---|---|---|---|---|
| `Chorizite.OpenGLSDLBackend/Lib/ObjectMeshManager.cs` | ~2079 | The hub: reads GfxObj/Setup/Palette from dats; decodes textures; manages GPU resources; modern-rendering global buffers | **yes** | yes | **T4** |
| `Chorizite.OpenGLSDLBackend/OpenGLGraphicsDevice.cs` | ~625 | Silk.NET wrapper: GL ctx, ProcessGLQueue, render-state | no | yes | **T3** |
| `Chorizite.OpenGLSDLBackend/Lib/TextureHelpers.cs` | ~variable | INDEX16 / P8 / A8 / DXT decode helpers (pure functions) | no | no | **T3** |
| `Chorizite.OpenGLSDLBackend/Lib/SceneryHelpers.cs` | small | `Displace` / `RotateObj` / `ScaleObj` / `ObjAlign` / `CheckSlope` (pure) | no | no | **T5** |
| Particle emitter + batcher (T4 supporting) | ~combined 1,200 | Particle pipeline used by ObjectMeshManager | maybe | yes | **T4** |
| Global mesh buffer (modern rendering) | ~500 | Single global VAO/VBO/IBO for modern path | no | yes | **T4** |
| ManagedGL{Texture, TextureArray, VertexBuffer, IndexBuffer, VertexArray, FrameBuffer, UniformBuffer} | ~150-300 ea | Per-resource GL lifecycle wrappers | no | yes | **T3** |
| `GLSLShader.cs`, `GLHelpers.cs`, `GLStateScope.cs` | ~100-300 ea | Shader compile + GL state utility | no | yes | **T3** |
| `Lib/ObjectRenderBatch.cs`, `Lib/ObjectRenderData.cs` | small | Per-batch + per-object render data structs | no | no | **T4** |
| `Lib/DebugRenderSettings.cs` | small | Settings shape passed to OpenGLGraphicsDevice ctor | no | no | **T3** |

**Approximate bucket totals across the full Chorizite closure** (28 files):

- **T3 (GL infra, no dat dep)**: 14 files, ~2,900 LOC
- **T4 (mesh pipeline)**: 8 files, ~3,313 LOC (ObjectMeshManager dominates)
- **T5 stateless (SceneryHelpers + helpers)**: ~2 files, ~200 LOC
- **NOT-CORE leaves** (TextureHelpers, EdgeLineBuilder, extensions): 4 files, ~400 LOC — note these still get extracted, they're just leaves not in the deep call graph of ObjectMeshManager

---

## 4. Extraction-task mapping

Reconciling agent findings against spec §4 — **three of the spec's listed
extractions are unnecessary because the files turned out not to be
reachable**.

### Reachable, must extract

| Bucket | Files (count, LOC) | Spec §4 alignment |
|---|---|---|
| **T3 — Texture / GL infrastructure** | ~15 files, ~3,100 LOC (14 from Chorizite + `TextureHelpers`) | Matches spec §4.2/§4.3: `TextureHelpers`, `OpenGLGraphicsDevice`, plus all `ManagedGL*` wrappers, `GLSLShader`, `GLHelpers`, `GLStateScope` |
| **T4 — Mesh pipeline** | 8 files, ~3,313 LOC | Matches spec §4.1: `ObjectMeshManager`, `ObjectRenderBatch`, `ObjectRenderData` + transitive supports (`GlobalMeshBuffer`, particle batcher, modern render data) |
| **T5 — Scenery + terrain stateless** | 5 files, ~782 LOC (`SceneryHelpers`, `TerrainUtils`, `TerrainEntry`, `CellSplitDirection`, possibly `RegionInfo` if reachable) | **Smaller than spec §4.1+§4.2 anticipated** — `LandSurfaceManager` and `SceneryRenderManager` are NOT in our closure (we have our own ports / our own renderer) |

### Files spec listed but we do NOT need

| File (spec §4 reference) | Status | Why |
|---|---|---|
| `Chorizite.OpenGLSDLBackend.Lib.LandSurfaceManager` (spec §4.2) | **NOT EXTRACTED** | Acdream has its own `src/AcDream.Core/Terrain/TerrainBlending.cs` (line 8: "Ported from WorldBuilder.LandSurfaceManager"). No `using` import of WB's class. Confirm in T2: kick or keep our port. |
| `Chorizite.OpenGLSDLBackend.Lib.SceneryRenderManager` (spec §4.1) | **NOT EXTRACTED** | `SceneryGenerator.cs` uses only `SceneryHelpers` (stateless). No reference to `SceneryRenderManager` (the WB-internal pipeline class). |
| `Chorizite.OpenGLSDLBackend.Lib.EnvCellRenderManager` (spec §4.4) | **NOT EXTRACTED** | Acdream renders EnvCells via `ObjectMeshManager` (its `PrepareEnvCellMeshData` path) + `WbDrawDispatcher`. WB's `EnvCellRenderManager` is the editor's separate render path and is not referenced. |
| `Chorizite.OpenGLSDLBackend.Lib.PortalRenderManager` (spec §4.4) | **NOT EXTRACTED** | Acdream renders portals via the same mesh path; WB's `PortalRenderManager` is editor-only. |
| `Chorizite.OpenGLSDLBackend.Lib.TerrainRenderManager` (spec §9.2 open Q) | **NOT EXTRACTED** | Confirmed: we use `src/AcDream.App/Rendering/TerrainModernRenderer.cs` (Phase N.5b ported retail's `FSplitNESW`). Spec §9.2's recommendation to leave WB's `TerrainRenderManager` in `references/` matches the closure finding. |

**This means the spec's T5 and T6 buckets shrink dramatically:**

- **Spec T5** was "scenery + terrain pipelines" (~3 named files: `SceneryHelpers`, `SceneryRenderManager`, `LandSurfaceManager`). Real T5 is just `SceneryHelpers` (stateless utility, ~100 LOC) + the 4 WB.Shared terrain helpers (`TerrainUtils`, `TerrainEntry`, `CellSplitDirection`).
- **Spec T6** was "EnvCell + portal renderers". Real T6 is **empty**. Recommend dropping T6 from the task plan.

### Files we explicitly DROP (REPLACE bucket)

- `WorldBuilder.Shared/Services/IDatReaderWriter.cs` (137 LOC) — interface goes away.
- `WorldBuilder.Shared/Services/DefaultDatReaderWriter.cs` (215 LOC) — concrete impl goes away.
- The `_wbDats = new DefaultDatReaderWriter(datDir)` line in `WbMeshAdapter.cs:79` and its `Dispose()` partner at line 346 — removed in T7.
- The cross-check diagnostic at `WbMeshAdapter.cs:224-262` (`[indoor-upload] NULL_RESULT`) — removed in T7; depends on `ResolveId` which goes away with `DefaultDatReaderWriter`.

---

## 5. Hidden-dependency risks

### 5.1 Internal types

**Three internal types need `internal` → `public` promotion** when copied
across the project boundary (or copied with their owners and kept
internal in the new project):

- `EmbeddedResourceReader` (Chorizite) — referenced by ObjectMeshManager
  for embedded shader / resource loads.
- `TextureFormatExtensions` (Chorizite) — extension methods on
  `PixelFormat`.
- `BufferUsageExtensions` (Chorizite) — extension methods on
  `BufferUsageARB`.

No internal types in `WorldBuilder.Shared`'s closure. Test mocks marked
`internal` are not in scope (they live in test projects).

### 5.2 Source generators / build hooks

**None found.** Neither `Chorizite.OpenGLSDLBackend.csproj` nor
`WorldBuilder.Shared.csproj` has `<Target>`, `<AnalyzerReference>`, or
source-generator `<PackageReference>` entries. Verbatim file copy will
transfer cleanly.

### 5.3 Resource files

**Shaders.** Reachable closure loads a minority of the 21 shaders in
`Chorizite.OpenGLSDLBackend/Shaders/`. The Chorizite-closure agent
identified only **2 shaders loaded by ObjectMeshManager's path**:

- `Shaders/Particle.vert`
- `Shaders/Particle.frag`

(All other shaders — `Landscape`, `StaticObject*`, `PortalStencil`,
`Outline`, `InstancedLine`, `Text`, `UI`, `Gizmo` — are loaded by render
managers we don't use, e.g. `StaticObjectRenderManager`,
`TerrainRenderManager`, `PortalRenderManager`. Those managers are NOT in
our closure.) **Acdream's own entity shader is `mesh_modern.{vert,frag}`
in `src/AcDream.App/Rendering/Shaders/` — not a WB asset.**

This contradicts the broader-sweep agent's count of "10 shader files
actively referenced", which was including managers we don't use.
**Verify in T4** by grepping for `LoadShader` string literals inside
ObjectMeshManager + its transitive closure.

**Fonts.** Two `.ttf` files exist in `Chorizite.OpenGLSDLBackend/Fonts/`
(DroidSans + DroidSans-Bold). They are used by `FontRenderer`, which is
**NOT in our closure** (we use BitmapFont + StbTrueTypeSharp + ImGui).
**Fonts do not need to be copied.**

**Other.** No PNG, JSON, XML, or other asset files referenced by the
closure beyond shaders.

### 5.4 NuGet dependencies that transfer with the extraction

Files in the closure reference:

- `Silk.NET.OpenGL` (already a `<PackageReference>` in `AcDream.App.csproj`)
- `BCnEncoder.Net` (already in `AcDream.Core.csproj` v2.2.1 — Chorizite
  uses v2.2.x, compatible)
- `SixLabors.ImageSharp` (NOT currently in acdream — needs adding for
  some of WB's texture-load paths; **verify in T4 whether our extraction
  closure actually touches ImageSharp** — if only for the editor scene
  loads, we can drop)
- `MemoryPack` — `TerrainEntry` uses `[MemoryPackable]`. **Add as
  `<PackageReference>` in `AcDream.Core.csproj` at T5**, or strip the
  attribute (it's used only by WB's editor save/load; acdream doesn't
  serialize `TerrainEntry`). Stripping is the simpler call.

### 5.5 DefaultDatReaderWriter ↔ DatCollection — the real risk

This is the only material risk in the whole audit. WB's interface and
acdream's `DatCollection` diverge in several ways:

| Concern | WB | Acdream's `DatCollection` (per probe at `WbMeshAdapter.cs:228-260`) |
|---|---|---|
| **Database surface** | Returns `IDatDatabase` (abstract interface) for `Portal`, `HighRes`, `Language`, `Cell` | Concrete types (`PortalDatabase`, `CellDatabase`, `LocalDatabase`) |
| **Cell databases** | `CellRegions` dictionary (multi-region cell support; auto-discovers at construction) | Single `Cell` property |
| **Cross-database lookup** | `ResolveId(uint id)` returns `IEnumerable<(database, type)>` (HighRes → Portal → Language → all Cells) | **No equivalent.** WbMeshAdapter's diagnostic code uses it; production mesh path may or may not |
| **Per-type caching** | `ConcurrentDictionary<(Type, uint), IDBObj>` per database; thread-safe by design | Documented as **NOT thread-safe** (per `memory/feedback_phase_a1_hotfix_saga.md`) — but with the O-Q1 verdict (render-thread only), this may be fine |
| **Iteration tracking** | Per-database iteration counters | Verify in T4 |
| **Write support** | `TrySave` overloads | Acdream is read-only (correct for a client) |
| **File-handle sharing** | Today each reader opens its own 4 files (~50-100 MB index duplication, the smell Phase O closes) | The DatCollection's file handles are reused; T7 ends the duplication |

**Pre-T4 verification checklist:**

1. Grep `ObjectMeshManager.cs` for every `_dats.X` call site. Catalog
   which methods/properties of `IDatReaderWriter` it actually uses.
2. For each, confirm `DatCollection` has the equivalent (or design the
   shim).
3. Decide whether `ResolveId` is needed in production code or is purely
   diagnostic (the `[indoor-upload]` probe is the only known caller).
   If diagnostic-only: drop it at T7.
4. Confirm the multi-region `CellRegions` story — does WB's
   `ObjectMeshManager` actually iterate `CellRegions`, or always call
   `Cell` singular? (If singular, the dict→single mapping is trivial.)
5. Confirm thread-safety: with O-Q1 settled (render-thread only), do we
   still need `ConcurrentDictionary` semantics, or can our existing
   single-thread `DatCollection` serve?

---

## 6. Thread-model finding (Open Question O-Q1)

**Verdict: SAFE — verified by code inspection.** No worker-thread access
to WB code today, so extraction does not expose a latent race.

Evidence (paths from the worktree root):

- `WbMeshAdapter.Tick()` is documented in the source as render-thread only
  ([src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs:275-278](src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs:275)).
- `LandblockSpawnAdapter` and `EntitySpawnAdapter` are called only from
  `GpuWorldState` methods (`AddLandblock`, `RemoveLandblock`,
  `AddEntitiesToExistingLandblock`, `RemoveEntitiesFromLandblock`,
  `OnCreate`, `OnRemove`). `GpuWorldState` is the render-thread entity
  state manager (per `CLAUDE.md` two-tier streaming arch); the streaming
  worker thread (`LandblockStreamer`) never touches WB.
- The two-tier streaming spec
  (`docs/superpowers/specs/2026-05-09-phase-a5-two-tier-streaming-design.md`)
  is explicit that the worker thread builds dat-decoded `LandblockState`
  objects and hands them to the render thread via a completion queue;
  WB's `ObjectMeshManager` is never invoked from the worker.
- WB's own `ObjectMeshManager` uses `ConcurrentDictionary` for
  `_usageCount` and an explicit `lock(_lruList)` — defense in depth that
  doesn't matter for us today, but keeps the door open if a future
  acdream design hands streaming work to a worker.

**Action for the spec:** §9.1 (O-Q1) can be marked closed with
"Verified safe; render-thread-only access; ConcurrentDictionary in WB
adds belt-and-braces."

---

## 7. Surprises / sharp edges

1. **Spec §4 over-specifies the extraction.** Three named files
   (`LandSurfaceManager`, `EnvCellRenderManager`, `PortalRenderManager`)
   and one open question (§9.2's `TerrainRenderManager`) are NOT in the
   actual closure. The spec should be amended: T5 shrinks to "stateless
   scenery + terrain helpers" (5 files, ~800 LOC); T6 disappears entirely.

2. **The biggest risk is API-shape mismatch, not file count.**
   `IDatReaderWriter` returns interface types and has multi-region cell
   support + a `ResolveId` cross-DB search; `DatCollection` returns
   concrete types and has a single cell property. T4 must design the
   shim layer (or refactor `ObjectMeshManager` slightly to use our
   shape — slightly violates "verbatim copy" but is one-call narrow).

3. **`SixLabors.ImageSharp` is a possibly-unnecessary new NuGet
   dependency.** If our closure actually touches it (some texture-load
   helpers in Chorizite use ImageSharp for PNG/JPG), T4 needs to add it.
   If it's only in editor-load paths we don't use, T4 can drop the
   imports. **Verify before T2.**

4. **`MemoryPack` is a small new dep for one attribute on
   `TerrainEntry`.** Cheapest answer: strip the attribute when copying
   `TerrainEntry.cs` into our tree (we don't serialize the struct).

5. **The `[indoor-upload]` diagnostic in `WbMeshAdapter.cs:192-263`
   becomes vestigial after T7.** It depends on `ResolveId` (which goes
   away), it compares `_wbDats` (which goes away) against `_dats`. The
   whole `if (RenderingDiagnostics.IsEnvCellId(id) && ...)` block + its
   `_pendingEnvCellRequests` companion can be deleted in T7.

6. **`SplitFormulaDivergenceTest.cs` is safely deletable** after T5
   ships. It was a one-time data-collection test that informed the
   N.5b path-C decision; its findings are baked into the codebase. We
   can drop the WB-types import by deleting the test.

7. **Total `LOC of extracted code: ~6.0-6.5K`** (excluding the 350 LOC
   of `IDatReaderWriter`/`DefaultDatReaderWriter` which are deleted, and
   excluding the 600 LOC of NOT-CORE leaves the broader agent
   classified as "optional" but on review still need to come along).
   This is somewhat **larger than the spec's implicit ~5K assumption**
   but only marginally; the 7-8 day estimate still looks right.

---

## 8. Open questions for the user (to call before T2 starts)

1. **Spec §4 amendment.** Confirm that the three not-reachable
   components (`LandSurfaceManager`, `EnvCellRenderManager`,
   `PortalRenderManager`) are dropped from the extraction task list. If
   yes, T6 disappears entirely and T5 narrows to stateless helpers.
   *Recommendation: drop them. Saves ~1.5 days of T6 work that would
   otherwise be a no-op anyway.*

2. **NuGet additions.** Are we OK adding `MemoryPack` and (potentially)
   `SixLabors.ImageSharp` as new `<PackageReference>` lines in
   `AcDream.Core.csproj`? Or do we prefer stripping the `[MemoryPackable]`
   attribute from `TerrainEntry` and dropping ImageSharp-touching code
   from the extraction?
   *Recommendation: strip `MemoryPack` (one-line change). On ImageSharp,
   wait until T4 confirms whether the closure actually touches it.*

3. **`DatCollection` design call.** Two paths for the dat-swap:
   - **A) Adapter shim**: write a thin `DatCollectionAsIDatReaderWriter`
     adapter so `ObjectMeshManager` keeps its current dat field type
     and we preserve "verbatim copy" discipline.
   - **B) Refactor**: change `ObjectMeshManager`'s field type to
     `DatCollection` and rewrite ~5-20 call sites inside it.
   *Recommendation: A) for the first pass (preserves "verbatim copy"
   discipline; spec O-D1 says no improvements). A follow-up phase can
   refactor (B) once the extraction is settled.*

4. **`ResolveId` fate.** Drop it (diagnostic-only) or implement an
   equivalent on `DatCollection`?
   *Recommendation: drop. The `[indoor-upload]` diagnostic was a Phase-2
   investigation tool that has done its job.*

5. **Test deletion.** OK to delete `SplitFormulaDivergenceTest.cs` (the
   one-time data-collection sweep) as part of T7's WB-reference drop?
   `TextureDecodeConformanceTests.cs` should stay (genuine ongoing
   conformance for our `SurfaceDecoder`).
   *Recommendation: delete the SplitFormula test; keep the texture
   conformance tests.*

6. **Confirmation on 7-8 day estimate.** Given (a) T6 disappears and
   (b) T5 shrinks, the bulk of the effort is T3 (~1d) + T4 (~2d
   including the dat-shim design) + T7 (~0.5d). Net estimate looks
   **closer to 5-6 days** of pure extraction work, plus the spec's
   1d verification gate and 0.5d ship.
   *Recommendation: keep the 7-8 day envelope as scheduled time
   (includes inevitable mid-work surprise budget); call it 5-6d of
   focused engineering plus 1-2d of verification.*

---

## 9. Acceptance recap (per the prompt)

- [x] Audit doc exists at the agreed path.
- [x] Every file in the closure is bucketed (T3 / T4 / T5 / T6 / NOT /
      REPLACE).
- [x] Hidden-dependency risks section addresses internal types, source
      generators, resource files, and `DefaultDatReaderWriter` vs
      `DatCollection` semantic diff.
- [x] No source code edited. No `dotnet build/test`. No project files
      touched. (Verify with `git status` — only this audit doc + a few
      research subreports the parallel agents would have written if they
      had Write access; in practice only this single file is on disk.)
- [x] Thread-model finding included.
- [x] Open questions enumerated.

---

## 10. TL;DR for the user (the four asks from the handoff prompt)

1. **Total LOC of the closure:** ~7,705 LOC across 33 files
   (~6,829 Chorizite + ~876 WB.Shared).

2. **Bucket breakdown:**
   - T3 (GL infra): ~15 files, ~3,100 LOC.
   - T4 (mesh): 8 files, ~3,313 LOC.
   - T5 (scenery + terrain stateless helpers): 5 files, ~782 LOC.
   - T6 (EnvCell / portal renderers): **0 files** — not reachable.
   - REPLACE (delete, swap to DatCollection): 2 files, 352 LOC.

3. **Sharp edges:**
   - Three spec §4 components turn out not to be reachable — recommend
     dropping `LandSurfaceManager`, `EnvCellRenderManager`,
     `PortalRenderManager` from the extraction plan.
   - The real risk is the `IDatReaderWriter` ↔ `DatCollection` API
     mismatch: interface vs concrete return types, multi-region cell
     dict vs single cell, `ResolveId` cross-DB search not in our
     reader. Needs a shim layer or a narrow refactor at T4.
   - Three internal types in Chorizite need `internal → public`
     promotion (`EmbeddedResourceReader`, `TextureFormatExtensions`,
     `BufferUsageExtensions`).
   - `MemoryPack` and possibly `SixLabors.ImageSharp` are new NuGet
     deps if we don't strip them out.
   - The `[indoor-upload]` diagnostic in `WbMeshAdapter` and the
     `SplitFormulaDivergenceTest` test become deletable at T7.

4. **Honest read on the 7-8 day estimate:** Reasonable, probably
   slightly conservative. Net extraction work is ~5-6 days of focused
   engineering; the spec's verification + ship time bring it to
   7-8 days total. The shrinkage of T5 + disappearance of T6 buys
   margin against the dat-shim design work at T4 — net-net the
   estimate holds.
