# Phase O — DatPath Unification Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Extract the load-bearing WorldBuilder code we use into acdream's own source tree, route all dat reads through our `DatCollection`, and drop the two `` entries that pull in WB. End state: ONE dat reader in the process, ZERO references to `WorldBuilder.*` / `Chorizite.OpenGLSDLBackend.*` namespaces in our source. **Architecture:** Verbatim copy of ~33 WB files (~7.7K LOC) split across two layers — `src/AcDream.Core/Rendering/Wb/` for pure-CPU helpers (`TextureHelpers`, `SceneryHelpers`, `TerrainUtils`, `TerrainEntry`, `CellSplitDirection`); `src/AcDream.App/Rendering/Wb/` for everything that touches GL (`OpenGLGraphicsDevice`, `ManagedGL*`, `GLSLShader`, `GLHelpers`, `GLStateScope`, `ObjectMeshManager`, particle pipeline, render data structs). `ObjectMeshManager` refactored to take our `DatCollection` directly (with fallback to a thin adapter if the call-site count exceeds 20). Three `internal` types in Chorizite promoted to `public`. **Tech Stack:** C# .NET 10, Silk.NET.OpenGL, BCnEncoder.Net, Chorizite.DatReaderWriter (NuGet — stays). MIT attribution required for the extracted code per WB's license. **Spec:** [`docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md`](../specs/2026-05-21-phase-o-dat-path-unification-design.md) **Audit:** [`docs/research/2026-05-21-phase-o-t1-wb-audit.md`](../../research/2026-05-21-phase-o-t1-wb-audit.md) --- ## File Structure ### Created during this phase **`src/AcDream.Core/Rendering/Wb/` (new directory):** - `TextureHelpers.cs` — pure pixel-format decoders (`Fill*` helpers for INDEX16/P8/A8/A8R8G8B8/R8G8B8/R5G6B5/A4R4G4B4/etc.). - `SceneryHelpers.cs` — stateless scenery transformations (`Displace`, `RotateObj`, `ScaleObj`, `ObjAlign`, `CheckSlope`). - `TerrainUtils.cs` — terrain math (`GetHeight`, `GetNormal`, `OnRoad`, `CalculateSplitDirection`). - `TerrainEntry.cs` — packed per-vertex terrain struct (`[MemoryPackable]` attribute STRIPPED). - `CellSplitDirection.cs` — `SWtoNE` / `SEtoNW` enum. **`src/AcDream.App/Rendering/Wb/` (existing directory, ~25 files added):** - `OpenGLGraphicsDevice.cs` — Silk.NET wrapper, GL context, `ProcessGLQueue`. - `GLSLShader.cs`, `GLHelpers.cs`, `GLStateScope.cs` — shader + state wrappers. - `ManagedGLTexture.cs`, `ManagedGLTextureArray.cs`, `ManagedGLVertexBuffer.cs`, `ManagedGLIndexBuffer.cs`, `ManagedGLVertexArray.cs`, `ManagedGLFrameBuffer.cs`, `ManagedGLUniformBuffer.cs` — GL resource wrappers (7 files). - `ObjectMeshManager.cs` — mesh extraction + GPU upload (REFACTORED to take `DatCollection`). - `ObjectRenderBatch.cs`, `ObjectRenderData.cs` — render-data structs. - `DebugRenderSettings.cs` — settings struct for `OpenGLGraphicsDevice` ctor. - Particle batcher + emitter files (count + names confirmed during T4 closure walk). - `GlobalMeshBuffer.cs`, modern render data structs (count + names confirmed during T4 closure walk). - `EmbeddedResourceReader.cs`, `TextureFormatExtensions.cs`, `BufferUsageExtensions.cs` — three `internal` types promoted to `public`. **Repo root:** `NOTICE.md` updated (or created) with WB MIT attribution. ### Modified during this phase - `src/AcDream.Core/AcDream.Core.csproj` — drop 2 `` lines. - `src/AcDream.App/AcDream.App.csproj` — drop 2 `` lines. - `src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs` — drop `_wbDats` field/ctor/dispose; drop `[indoor-upload] NULL_RESULT` block (lines 192-263 currently); simplify ctor to pass `DatCollection` directly to `ObjectMeshManager`. - `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` — update `using Chorizite.OpenGLSDLBackend.Lib;` → `using AcDream.App.Rendering.Wb;`. - `src/AcDream.Core/World/WbSceneryAdapter.cs` — update `using WorldBuilder.Shared.Models;` → `using AcDream.Core.Rendering.Wb;`. - `src/AcDream.Core/World/SceneryGenerator.cs` — update both `using` lines to new namespaces. - `src/AcDream.Core/Textures/SurfaceDecoder.cs` — update `using Chorizite.OpenGLSDLBackend.Lib;` → `using AcDream.Core.Rendering.Wb;`. - `tests/AcDream.Core.Tests/Textures/TextureDecodeConformanceTests.cs` — update `using` line. - `tests/AcDream.Core.Tests/World/WbSceneryAdapterTests.cs` — doc-comment fix only. - `tests/AcDream.Core.Tests/World/SceneryGeneratorTests.cs` — doc-comment fix only. - `tests/AcDream.Core.Tests/Rendering/Wb/MeshExtractionConformanceTests.cs` — doc-comment fix only. - `tests/AcDream.Core.Tests/Terrain/ClientReference.cs`, `ClientConformanceTests.cs` — doc-comment fix only. - `CLAUDE.md` — rewrite the "WB integration cribs" section. - `docs/architecture/worldbuilder-inventory.md` — update ownership notes. ### Deleted during this phase - `tests/AcDream.Core.Tests/Terrain/SplitFormulaDivergenceTest.cs` — one-time data-collection test, job done at N.5b. --- ## Task 1: Setup (was O-T2) **Files:** - Create: `src/AcDream.Core/Rendering/Wb/` (directory only — no files yet) - Create/Modify: `NOTICE.md` (repo root) The `src/AcDream.App/Rendering/Wb/` directory already exists. - [ ] **Step 1: Create the Core/Rendering/Wb directory** Run: ```powershell New-Item -ItemType Directory -Force -Path "src/AcDream.Core/Rendering/Wb" ``` Expected: directory created, no error. - [ ] **Step 2: Check whether NOTICE.md exists** Run: ```powershell Test-Path "NOTICE.md" ``` Expected: `True` or `False`. If `True`, read it before editing. If `False`, the next step creates it. - [ ] **Step 3: Create or append the WorldBuilder attribution section in NOTICE.md** Write to `NOTICE.md` (append if exists, create otherwise): ```markdown ## WorldBuilder Portions of acdream's rendering and dat-handling code are copied from WorldBuilder (https://github.com/Chorizite/WorldBuilder), MIT-licensed. The extracted code lives under: - `src/AcDream.Core/Rendering/Wb/` — pure helpers (texture decode, scenery transforms, terrain math). - `src/AcDream.App/Rendering/Wb/` — GL infrastructure and mesh pipeline. Original copyright holders: Chorizite contributors (see WorldBuilder's LICENSE file). Adapted by acdream maintainers to consume our `DatCollection` directly (replacing WB's `DefaultDatReaderWriter`) and to remove editor-only code paths. Original MIT license text: [Insert full MIT license body here from references/WorldBuilder/LICENSE — verify the file exists at that path and use its exact content] ``` If the user has a different `NOTICE.md` style, follow theirs; this is the minimum content. - [ ] **Step 4: Verify the MIT license body** Run: ```powershell Test-Path "references/WorldBuilder/LICENSE" Get-Content "references/WorldBuilder/LICENSE" -TotalCount 5 ``` Expected: file exists; first few lines should be MIT license text. Copy the full LICENSE body into NOTICE.md, replacing the `[Insert ...]` placeholder. - [ ] **Step 5: Commit** ```powershell git add NOTICE.md git add "src/AcDream.Core/Rendering/Wb/.gitkeep" # if needed to track empty dir git commit -m @' chore(O-T2): create Core/Rendering/Wb directory + NOTICE.md attribution Phase O setup: extracted-WB code home + MIT attribution per O-D5. Spec: docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md Co-Authored-By: Claude Opus 4.7 (1M context) '@ ``` Note: `New-Item -ItemType Directory` doesn't track empty dirs in git. If T1 needs the directory to exist before later commits, write a placeholder file (e.g., `.gitkeep` or a tiny `README.md` describing the directory's purpose) and commit it. --- ## Task 2: Extract pure stateless helpers to Core (was O-T5) Moved earlier in the plan from spec's T5 position because: (a) lowest-risk extraction (pure functions, no GL, no dat behavior change); (b) unblocks the namespace migrations in our own Core files; (c) catches any namespace-update gotchas before the load-bearing T4. **Files:** - Create: `src/AcDream.Core/Rendering/Wb/TextureHelpers.cs` - Create: `src/AcDream.Core/Rendering/Wb/SceneryHelpers.cs` - Create: `src/AcDream.Core/Rendering/Wb/TerrainUtils.cs` - Create: `src/AcDream.Core/Rendering/Wb/TerrainEntry.cs` - Create: `src/AcDream.Core/Rendering/Wb/CellSplitDirection.cs` - Modify: `src/AcDream.Core/World/SceneryGenerator.cs` (2 `using` lines) - Modify: `src/AcDream.Core/World/WbSceneryAdapter.cs` (1 `using` line) - Modify: `src/AcDream.Core/Textures/SurfaceDecoder.cs` (1 `using` line) - Modify: `tests/AcDream.Core.Tests/Textures/TextureDecodeConformanceTests.cs` (1 `using` line) - Modify: `tests/AcDream.Core.Tests/Terrain/SplitFormulaDivergenceTest.cs` — actually this gets DELETED at T7, but its imports still need to work between now and then OR the test is moved to deleted-list earlier. Decision: leave it broken-but-doesn't-affect-other-tests for now, delete cleanly at T7. - [ ] **Step 1: Read each of the 5 WB source files** Read: - `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/TextureHelpers.cs` - `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/SceneryHelpers.cs` - `references/WorldBuilder/WorldBuilder.Shared/Modules/Landscape/Lib/TerrainUtils.cs` - `references/WorldBuilder/WorldBuilder.Shared/Modules/Landscape/Models/TerrainEntry.cs` - `references/WorldBuilder/WorldBuilder.Shared/Modules/Landscape/Models/CellSplitDirection.cs` (Note: the worktree's `references/WorldBuilder` submodule is empty. Use the main checkout at `/c/Users/erikn/source/repos/acdream/references/WorldBuilder/` if needed.) Confirm each file is what the audit expected. Note any unexpected dependencies (other WB types referenced beyond NuGet packages like Silk.NET, BCnEncoder.Net, MemoryPack, etc.). - [ ] **Step 2: Copy `TextureHelpers.cs` to Core** Copy `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/TextureHelpers.cs` to `src/AcDream.Core/Rendering/Wb/TextureHelpers.cs`. Then update the file: Change the namespace declaration line from: ```csharp namespace Chorizite.OpenGLSDLBackend.Lib; ``` to: ```csharp namespace AcDream.Core.Rendering.Wb; ``` If TextureHelpers has any `using` statements pointing to other WB types, note them but DO NOT change yet — copy is verbatim except for the namespace. - [ ] **Step 3: Copy `SceneryHelpers.cs` to Core (same process)** Same as Step 2 for `SceneryHelpers.cs`. Update its namespace to `AcDream.Core.Rendering.Wb`. If `SceneryHelpers.cs` has a `using WorldBuilder.Shared.Modules.Landscape.Lib;` or similar for `TerrainUtils`, leave it — we'll update it at Step 6. - [ ] **Step 4: Copy `TerrainUtils.cs` to Core** Same. Update namespace from `WorldBuilder.Shared.Modules.Landscape.Lib` → `AcDream.Core.Rendering.Wb`. - [ ] **Step 5: Copy `TerrainEntry.cs` to Core + strip `[MemoryPackable]`** Copy. Update namespace. Then **strip the `[MemoryPackable]` attribute** + any `[MemoryPack...]` attributes on fields: ```csharp // BEFORE [MemoryPackable] public partial struct TerrainEntry { ... } // AFTER public struct TerrainEntry { ... } ``` Drop the `partial` modifier as well if the only reason it's `partial` is the MemoryPack source generator (verify by checking whether any other file extends the type via a separate `partial`). Drop any `using MemoryPack;` line. - [ ] **Step 6: Copy `CellSplitDirection.cs` to Core** Enum. Update namespace. Nothing else. - [ ] **Step 7: Update internal cross-references in the new files** After all 5 files exist in `src/AcDream.Core/Rendering/Wb/`: If `SceneryHelpers.cs` had `using WorldBuilder.Shared.Modules.Landscape.Lib;` (for `TerrainUtils`), change to nothing — they're in the same namespace now. If `TerrainUtils.cs` had `using WorldBuilder.Shared.Modules.Landscape.Models;` (for `TerrainEntry` / `CellSplitDirection`), change to nothing — same namespace. Run: ``` Grep pattern="using (WorldBuilder|Chorizite\.OpenGLSDLBackend)" path="src/AcDream.Core/Rendering/Wb" ``` Expected: no matches. If any, that file's still pointing at the old namespace; fix. - [ ] **Step 8: Update our own Core source files' `using` lines** Three Core files import WB types directly: In `src/AcDream.Core/World/WbSceneryAdapter.cs:2`: ```csharp // BEFORE using WorldBuilder.Shared.Models; // AFTER using AcDream.Core.Rendering.Wb; ``` Wait — `WbSceneryAdapter.cs:2` actually says `using WorldBuilder.Shared.Models;` not `...Modules.Landscape.Models;`. Verify the actual `using` and update accordingly. The import is for `TerrainEntry`. After T2 step 6, `TerrainEntry` is in `AcDream.Core.Rendering.Wb`. In `src/AcDream.Core/World/SceneryGenerator.cs:2` and `:6`: ```csharp // BEFORE using Chorizite.OpenGLSDLBackend.Lib; using WorldBuilder.Shared.Modules.Landscape.Lib; // AFTER using AcDream.Core.Rendering.Wb; // (single using replaces both, since both target types now live in the new namespace) ``` In `src/AcDream.Core/Textures/SurfaceDecoder.cs:3`: ```csharp // BEFORE using Chorizite.OpenGLSDLBackend.Lib; // AFTER using AcDream.Core.Rendering.Wb; ``` - [ ] **Step 9: Update conformance test's `using`** In `tests/AcDream.Core.Tests/Textures/TextureDecodeConformanceTests.cs:1`: ```csharp // BEFORE using Chorizite.OpenGLSDLBackend.Lib; // AFTER using AcDream.Core.Rendering.Wb; ``` Leave `tests/AcDream.Core.Tests/Terrain/SplitFormulaDivergenceTest.cs` alone for now — it's deleted at T7. - [ ] **Step 10: Build solution; expect green (or only the SplitFormulaDivergenceTest broken)** Run: ``` dotnet build ``` Expected: green. The `SplitFormulaDivergenceTest.cs` still has its `using WbTerrainUtils = WorldBuilder.Shared.Modules.Landscape.Lib.TerrainUtils;` alias — but `WorldBuilder.Shared` is still project-referenced at this point, so it still compiles. If anything else fails, the namespace migration missed a file. Fix and rerun. - [ ] **Step 11: Run tests; expect green** Run: ``` dotnet test --no-build ``` Expected: green. Pay particular attention to `TextureDecodeConformanceTests` — those should still pass byte-for-byte after the namespace move. - [ ] **Step 12: Commit T5** ```powershell git add src/AcDream.Core/Rendering/Wb/ src/AcDream.Core/World/WbSceneryAdapter.cs src/AcDream.Core/World/SceneryGenerator.cs src/AcDream.Core/Textures/SurfaceDecoder.cs tests/AcDream.Core.Tests/Textures/TextureDecodeConformanceTests.cs git commit -m @' feat(O-T5): extract pure stateless helpers to AcDream.Core.Rendering.Wb Verbatim copy of 5 WorldBuilder files into src/AcDream.Core/Rendering/Wb/: - TextureHelpers.cs (pixel-format decoders, Chorizite Lib) - SceneryHelpers.cs (scenery transforms, Chorizite Lib) - TerrainUtils.cs, TerrainEntry.cs, CellSplitDirection.cs (WB.Shared Landscape) Namespace migrated from WorldBuilder.* / Chorizite.OpenGLSDLBackend.Lib to AcDream.Core.Rendering.Wb per O-D11. [MemoryPackable] stripped from TerrainEntry per O-D10 (we don't serialize the struct). Updated 3 source files + 1 test file to import from the new namespace. Verbatim discipline (O-D1): only namespace + MemoryPack attribute changed. All algorithm bodies byte-identical to upstream. Spec: docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md Co-Authored-By: Claude Opus 4.7 (1M context) '@ ``` --- ## Task 3: Extract GL infrastructure to App (was O-T3) **Files:** - Create: 11+ files under `src/AcDream.App/Rendering/Wb/` (exact count after closure walk) - Modify: none yet (the existing App `Wb/` adapter files don't import GL infra directly — they go through Silk.NET — but `WbMeshAdapter.cs` constructs `OpenGLGraphicsDevice` which it currently imports from `Chorizite.OpenGLSDLBackend`. That single import will be updated at T4 when `ObjectMeshManager` arrives.) - [ ] **Step 1: Walk the GL-infra closure** Read `references/WorldBuilder/Chorizite.OpenGLSDLBackend/OpenGLGraphicsDevice.cs` first. List every WB type it references (look at field types, method parameters, constructor calls, `using` statements). Then read each referenced WB type. Continue until closure is closed. Expected closure (per audit): - `OpenGLGraphicsDevice.cs` - `GLSLShader.cs`, `GLHelpers.cs`, `GLStateScope.cs` (likely in `Lib/`) - `ManagedGLTexture.cs`, `ManagedGLTextureArray.cs`, `ManagedGLVertexBuffer.cs`, `ManagedGLIndexBuffer.cs`, `ManagedGLVertexArray.cs`, `ManagedGLFrameBuffer.cs`, `ManagedGLUniformBuffer.cs` (root or `Lib/`) - `EmbeddedResourceReader.cs`, `TextureFormatExtensions.cs`, `BufferUsageExtensions.cs` (3 internals to promote) - `DebugRenderSettings.cs` (sometimes needed by `OpenGLGraphicsDevice` ctor) Write down the actual file list with paths. - [ ] **Step 2: Check for `SixLabors.ImageSharp` reachability** Run: ``` Grep pattern="SixLabors\.ImageSharp" path="references/WorldBuilder/Chorizite.OpenGLSDLBackend" ``` List every file in the closure that imports ImageSharp. If empty: document "ImageSharp not reachable" in the T3 commit message. Skip step 3. If non-empty: proceed to step 3. - [ ] **Step 3: Strip ImageSharp usage from the closure files (only if needed)** For each closure file that imports ImageSharp: - Identify the call sites (typically PNG/JPG load helpers). - Replace with our existing byte-handling or BCnEncoder calls. - Remove the `using SixLabors.ImageSharp;` line. If a file's ImageSharp usage cannot be cleanly removed (e.g., it's the file's main purpose), then either: (a) drop the file from the extraction (verify it's not called by our closure), or (b) add `SixLabors.ImageSharp` as a `` to `AcDream.App.csproj` and keep the file. Document the decision in the T3 commit. - [ ] **Step 4: Copy each GL-infra file to `src/AcDream.App/Rendering/Wb/`** For each file in the closure list: - Copy verbatim from WB source to `src/AcDream.App/Rendering/Wb/`. - Update the namespace from `Chorizite.OpenGLSDLBackend` (or `.Lib`) to `AcDream.App.Rendering.Wb`. - Update any `using Chorizite.OpenGLSDLBackend.Lib;` / `using Chorizite.OpenGLSDLBackend;` lines inside the copied files to point at the new namespace. - For the 3 internal types (`EmbeddedResourceReader`, `TextureFormatExtensions`, `BufferUsageExtensions`): change `internal class` / `internal static class` / etc. to `public class` / `public static class`. Suggested copy order (each follows the same pattern): managed wrappers first (lowest deps), then helpers, then `OpenGLGraphicsDevice` last (depends on all the rest). - [ ] **Step 5: Build, iterate until green** Run: ``` dotnet build ``` Common failures + fixes: - "The type or namespace name 'X' could not be found" → `using` was missed; add `using AcDream.App.Rendering.Wb;`. OR a transitive WB type wasn't copied — copy it. - "The type 'X' is defined in an assembly that is not referenced" → ImageSharp/MemoryPack/other NuGet — see step 3. - "Inconsistent accessibility" → an internal type isn't yet promoted to public — promote it. Iterate until green. - [ ] **Step 6: Run tests; expect green (no test count change)** Run: ``` dotnet test --no-build ``` Tests should pass; this task doesn't change behavior, only file location and namespace. - [ ] **Step 7: Commit T3** ```powershell git add src/AcDream.App/Rendering/Wb/ git commit -m @' feat(O-T3): extract GL infrastructure to AcDream.App.Rendering.Wb Verbatim copy of GL infra (~15 files) from Chorizite.OpenGLSDLBackend into src/AcDream.App/Rendering/Wb/: - OpenGLGraphicsDevice.cs, GLSLShader.cs, GLHelpers.cs, GLStateScope.cs - ManagedGL{Texture,TextureArray,VertexBuffer,IndexBuffer, VertexArray,FrameBuffer,UniformBuffer}.cs (7 files) - DebugRenderSettings.cs Three internal types promoted to public per O-D9: - EmbeddedResourceReader, TextureFormatExtensions, BufferUsageExtensions SixLabors.ImageSharp reachability: . Verbatim discipline (O-D1): namespace + access modifier changes only. All algorithm bodies byte-identical to upstream. Build green; tests green (no count change). Spec: docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md Co-Authored-By: Claude Opus 4.7 (1M context) '@ ``` --- ## Task 4: Extract mesh pipeline + dat refactor (was O-T4) The load-bearing task. Includes the `ObjectMeshManager` refactor or fallback to adapter, per O-D7. **Files:** - Create: `src/AcDream.App/Rendering/Wb/ObjectMeshManager.cs` - Create: `src/AcDream.App/Rendering/Wb/ObjectRenderBatch.cs` - Create: `src/AcDream.App/Rendering/Wb/ObjectRenderData.cs` - Create: ~5+ supporting files (particle batcher / emitter, global mesh buffer, modern render data — count + exact filenames during closure walk) - Modify: `src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs` (constructor + `using` updates; cleanup at T7) - [ ] **Step 1: Walk the mesh-pipeline closure** Read `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/ObjectMeshManager.cs` end-to-end. It's ~2K LOC; use Read with offsets if needed. List every WB type it references that ISN'T already in the GL-infra closure (T3) or stateless-helpers closure (T2). Expected new types in closure (per audit): - `ObjectRenderBatch`, `ObjectRenderData` - `GlobalMeshBuffer` (or similar; the single-VBO/IBO holder for modern rendering) - `ParticleBatcher`, `ParticleEmitterRenderer` (referenced from mesh path) - Modern render data structs (likely a couple of small files) Write the actual file list with paths. - [ ] **Step 2: Count the `_dats.X` call sites in `ObjectMeshManager.cs`** Run: ``` Grep pattern="_dats\." path="references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/ObjectMeshManager.cs" ``` Count the matches. **Branch on count:** - **≤ 20 sites**: take the refactor path (O-D7). Plan steps 3-7 cover this. - **> 20 sites**: take the adapter fallback. Plan steps 3-7 are skipped; instead, plan steps A-D below (after step 7). Document the actual count in the T4 commit message regardless. - [ ] **Step 3: (refactor path) Copy mesh-pipeline files to App, namespace-update** Copy each closure file to `src/AcDream.App/Rendering/Wb/`. Update each file's namespace from `Chorizite.OpenGLSDLBackend.Lib` → `AcDream.App.Rendering.Wb`. Update any internal `using` lines. Do NOT yet modify `ObjectMeshManager`'s dat type — that's the next step. First, the verbatim copy + namespace. - [ ] **Step 4: (refactor path) Refactor `ObjectMeshManager` dat field type** In the newly-copied `src/AcDream.App/Rendering/Wb/ObjectMeshManager.cs`: a) Drop the import: ```csharp // BEFORE using WorldBuilder.Shared.Services; ``` (plus any related; remove if present.) b) Add the import: ```csharp using AcDream.Core; // or wherever DatCollection lives — verify with: // Grep pattern="^namespace.*DatCollection" path="src/AcDream.Core" // or: // Grep pattern="public.*class DatCollection\b" path="src/AcDream.Core" ``` c) Change the field declaration from: ```csharp private readonly IDatReaderWriter _dats; ``` to: ```csharp private readonly DatCollection _dats; ``` d) Change the constructor parameter from `IDatReaderWriter dats` to `DatCollection dats`. e) Update each `_dats.X` call site. The most common shapes: - `_dats.Portal.TryGet(id, out var x)` — verify DatCollection exposes `Portal.TryGet`. If yes, no change. If no, see step 5. - `_dats.Cell.TryGet(id, out var x)` — verify the same. - `_dats.HighRes.TryGet(...)` — same. - `_dats.Language.TryGet(...)` — same. - `_dats.ResolveId(id)` — drop the call site if it's diagnostic-only (per O-D12). If it's load-bearing, see step 5. For each call site, either: (a) leave it alone (DatCollection has the equivalent), (b) replace with DatCollection's API shape, or (c) escalate to step 5 (DatCollection needs a new method). - [ ] **Step 5: (refactor path) Add missing methods to DatCollection if needed** If step 4 surfaced a call shape DatCollection doesn't support, add the method to `src/AcDream.Core/Dats/DatCollection.cs` (or wherever DatCollection lives — find via grep). DO NOT add methods speculatively — only add what `ObjectMeshManager` actually calls. Document each added method in the T4 commit. - [ ] **Step 6: (refactor path) Update `WbMeshAdapter` to construct `ObjectMeshManager` with `DatCollection`** In `src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs`: Update line 7-15 `using` lines: drop `using Chorizite.OpenGLSDLBackend;`, `using Chorizite.OpenGLSDLBackend.Lib;`, `using WorldBuilder.Shared.Models;`, `using WorldBuilder.Shared.Services;`. Add `using AcDream.App.Rendering.Wb;` (and `AcDream.Core.Rendering.Wb;` if needed for any of the now-Core types referenced). Update line 85-88 ObjectMeshManager construction. Today: ```csharp _meshManager = new ObjectMeshManager( _graphicsDevice, _wbDats, new ConsoleErrorLogger()); ``` Change to: ```csharp _meshManager = new ObjectMeshManager( _graphicsDevice, dats, // the DatCollection passed to WbMeshAdapter's ctor new ConsoleErrorLogger()); ``` Leave `_wbDats` field + its construction at line 79 + its disposal at line 346 in place for now — they're cleaned up at T7. Do NOT touch the `[indoor-upload] NULL_RESULT` block (lines 192-263) yet — also cleaned up at T7. - [ ] **Step 7: (refactor path) Build, iterate until green** Run: ``` dotnet build ``` Common failures: - `_dats.X` call shape doesn't match DatCollection — fix per step 4/5. - Missing `using` for a moved type — add. Iterate until green. - [ ] **Step A: (adapter fallback path, only if count > 20)** Create `src/AcDream.App/Rendering/Wb/DatCollectionAdapter.cs`: ```csharp using AcDream.Core; // or wherever DatCollection lives using DatReaderWriter; using DatReaderWriter.DBObjs; // ... add interfaces / types needed to satisfy IDatReaderWriter namespace AcDream.App.Rendering.Wb; /// /// Adapts our DatCollection to WB's IDatReaderWriter interface so /// the extracted ObjectMeshManager can consume it without internal /// refactoring. O-D7 fallback path — taken because ObjectMeshManager /// has >20 _dats.X call sites and the refactor was deemed too invasive /// for one ship-window. /// internal sealed class DatCollectionAdapter : IDatReaderWriter { private readonly DatCollection _dats; public DatCollectionAdapter(DatCollection dats) { ArgumentNullException.ThrowIfNull(dats); _dats = dats; } // Implement each member of IDatReaderWriter by delegating to _dats. // Members are determined by reading IDatReaderWriter.cs (which is // also copied into this directory at this step, since we no longer // have it via the WorldBuilder.Shared project reference). // ... } ``` You also need to copy `IDatReaderWriter.cs` from WB.Shared into `src/AcDream.App/Rendering/Wb/` (or define a slimmed-down version with only the members ObjectMeshManager uses). Document the choice. - [ ] **Step B: (adapter fallback) Wire the adapter in WbMeshAdapter** ```csharp // BEFORE (in WbMeshAdapter ctor) _meshManager = new ObjectMeshManager(_graphicsDevice, _wbDats, ...); // AFTER _meshManager = new ObjectMeshManager( _graphicsDevice, new DatCollectionAdapter(dats), ...); ``` - [ ] **Step C: (adapter fallback) Build, iterate until green** Same as step 7 but with the adapter in place. - [ ] **Step D: (adapter fallback) Document the choice** Note in the T4 commit message that the adapter path was taken, with the actual call-site count and the rationale (e.g., "37 call sites; 22 use methods DatCollection doesn't currently expose, escalating to a separate refactor phase"). - [ ] **Step 8: (both paths) Run tests; expect green** Run: ``` dotnet test --no-build ``` Expected: green. No test count regression. - [ ] **Step 9: (both paths) Commit T4** ```powershell git add src/AcDream.App/Rendering/Wb/ git add src/AcDream.Core/Dats/ # only if DatCollection grew methods at step 5 git commit -m @' feat(O-T4): extract mesh pipeline; dat-shim Verbatim copy of ObjectMeshManager + supports (~8 files, ~3.3K LOC) from Chorizite.OpenGLSDLBackend.Lib into src/AcDream.App/Rendering/Wb/. ObjectMeshManager._dats call-site count: . Path taken: OR . Rationale: . DatCollection additions (if any): . WbMeshAdapter updated to construct ObjectMeshManager via the new path. The _wbDats field + [indoor-upload] NULL_RESULT block stay in place for now; cleaned up at T7. SixLabors.ImageSharp: . Verbatim discipline (O-D1): namespace + dat-surface only. Mesh extraction, texture decode, particle pipeline byte-identical to upstream. Build green; tests green (no count change). Spec: docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md Co-Authored-By: Claude Opus 4.7 (1M context) '@ ``` --- ## Task 5: Drop project references + cleanup (was O-T7) **Files:** - Modify: `src/AcDream.App/AcDream.App.csproj` - Modify: `src/AcDream.Core/AcDream.Core.csproj` - Modify: `src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs` - Modify: `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` (`using` line) - Modify: doc-comment-only files in tests/ (5-6 files, comment edits) - Delete: `tests/AcDream.Core.Tests/Terrain/SplitFormulaDivergenceTest.cs` - [ ] **Step 1: Delete `_wbDats` field + construction from `WbMeshAdapter.cs`** Open `src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs`. Delete: a) Line 34 (the field declaration): ```csharp private readonly DefaultDatReaderWriter? _wbDats; ``` b) Line 79 (the construction): ```csharp _wbDats = new DefaultDatReaderWriter(datDir); ``` c) Line 346 (the disposal in `Dispose()`): ```csharp _wbDats?.Dispose(); ``` d) The `datDir` parameter — verify it's no longer used after `_wbDats` removal. If `OpenGLGraphicsDevice` ctor doesn't need it, remove `datDir` from `WbMeshAdapter`'s constructor signature + all call sites. If it's still needed for something else, keep it. - [ ] **Step 2: Delete the `[indoor-upload] NULL_RESULT` block** In `WbMeshAdapter.cs`, delete the block from line ~192 to line ~263 (the diagnostic block that uses `_wbDats.ResolveId(...)`). Also delete the `_pendingEnvCellRequests` field and its associated logic in `Tick()` (the `isPendingEnvCell` block at ~305-321). Result: `WbMeshAdapter.cs` shrinks substantially — should be down from 349 to ~250 LOC. - [ ] **Step 3: Update `WbMeshAdapter.cs` and `WbDrawDispatcher.cs` `using` lines** In `WbMeshAdapter.cs` (lines 7-15): - Drop: `using Chorizite.OpenGLSDLBackend;`, `using Chorizite.OpenGLSDLBackend.Lib;`, `using WorldBuilder.Shared.Models;`, `using WorldBuilder.Shared.Services;`, `using DatReaderWriter;`, `using DatReaderWriter.DBObjs;` (verify whether the last two are still needed — they probably are for `GfxObj` references in `PopulateMetadata`). - Add: `using AcDream.App.Rendering.Wb;` if not present. In `WbDrawDispatcher.cs` (line 9): - Drop: `using Chorizite.OpenGLSDLBackend.Lib;` - Add: `using AcDream.App.Rendering.Wb;` - [ ] **Step 4: Delete the `` lines in both csprojs** In `src/AcDream.App/AcDream.App.csproj`, delete lines 38-39 (the two `` entries to `WorldBuilder.Shared` and `Chorizite.OpenGLSDLBackend`). Also drop the comment immediately above them ("Phase N.4 Task 9: ...") since the reference rationale is now historical. In `src/AcDream.Core/AcDream.Core.csproj`, delete lines 27-28 (same two references). Also drop the multi-line comment immediately above ("Phase N: WorldBuilder is acdream's rendering + dat-handling base...") since the references are gone. - [ ] **Step 5: Delete `SplitFormulaDivergenceTest.cs`** ```powershell Remove-Item "tests/AcDream.Core.Tests/Terrain/SplitFormulaDivergenceTest.cs" ``` This was a one-time data-collection test; its job finished at N.5b ship. - [ ] **Step 6: Fix doc-only WB mentions in remaining test files** For each of these files, update comment-only references to WB to point at the extracted location instead. Files (per audit grep): - `tests/AcDream.Core.Tests/World/WbSceneryAdapterTests.cs:10-17` — mentions `WorldBuilder.Shared.Models.TerrainEntry`. Update to `AcDream.Core.Rendering.Wb.TerrainEntry`. - `tests/AcDream.Core.Tests/World/SceneryGeneratorTests.cs:9` — mentions WB helpers in comment. Update to refer to `AcDream.Core.Rendering.Wb`. - `tests/AcDream.Core.Tests/Rendering/Wb/MeshExtractionConformanceTests.cs:12` — mentions `WB's ObjectMeshManager`. Either update to extracted name OR keep with a clarifying comment "(formerly WB; now ours, see Phase O)". - `tests/AcDream.Core.Tests/Terrain/ClientReference.cs:11` and `ClientConformanceTests.cs:10` — both "Ported from WorldBuilder-ACME-Edition". These are historical attributions; leave them OR update phrasing. This is comment-edit work; no logic changes. - [ ] **Step 7: Build solution; expect green** Run: ``` dotnet build ``` Expected: green. If a `using WorldBuilder.*` or `using Chorizite.OpenGLSDLBackend*` was missed, the build will fail with "namespace not found." Find it and update. Also expect: if any file still references `IDatReaderWriter` / `DefaultDatReaderWriter` directly, the build fails. Grep: ``` Grep pattern="(IDatReaderWriter|DefaultDatReaderWriter)" path="src" ``` Should return zero matches. - [ ] **Step 8: Run tests; expect green minus deleted test count** Run: ``` dotnet test --no-build ``` Expected: green. The total test count drops by however many tests were in the deleted `SplitFormulaDivergenceTest.cs` (likely a single test or two — record the actual delta). - [ ] **Step 9: Verify the acceptance criteria for reference deletion** Run all three: ``` Grep pattern="(WorldBuilder|Chorizite\.OpenGLSDLBackend)" path="src/AcDream.App/AcDream.App.csproj" Grep pattern="(WorldBuilder|Chorizite\.OpenGLSDLBackend)" path="src/AcDream.Core/AcDream.Core.csproj" Grep pattern="^using (WorldBuilder|Chorizite\.OpenGLSDLBackend)" path="src" ``` Expected: all three return zero matches. ``` Grep pattern="DefaultDatReaderWriter" path="src" ``` Expected: zero matches. If any return matches: something was missed in this task. Fix before commit. - [ ] **Step 10: Commit T7** ```powershell git add src/AcDream.App/Rendering/Wb/WbMeshAdapter.cs src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs src/AcDream.App/AcDream.App.csproj src/AcDream.Core/AcDream.Core.csproj tests/ git rm tests/AcDream.Core.Tests/Terrain/SplitFormulaDivergenceTest.cs git commit -m @' feat(O-T7): drop WB project references; cleanup _wbDats + diagnostic block End of Phase O extraction: - Dropped entries to WorldBuilder.Shared and Chorizite.OpenGLSDLBackend from AcDream.App.csproj and AcDream.Core.csproj. - Deleted _wbDats field + ctor + dispose from WbMeshAdapter.cs. - Deleted [indoor-upload] NULL_RESULT diagnostic block (lines 192-263); the Phase 2 cell-resolution diagnostic served its purpose, and its dependency on _wbDats.ResolveId goes away with this commit (per O-D12). - Deleted SplitFormulaDivergenceTest.cs (one-time N.5b data-collection test; job done). - Updated comment-only WB mentions in 5 test files to point at the extracted namespace. Verified acceptance criteria: - Zero refs to WorldBuilder.* / Chorizite.OpenGLSDLBackend.* in any csproj. - Zero "using WorldBuilder.*" / "using Chorizite.OpenGLSDLBackend*" in src/. - DefaultDatReaderWriter referenced in zero places. Build green; tests green (count: - from deleted test, no regressions). Spec: docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md Co-Authored-By: Claude Opus 4.7 (1M context) '@ ``` --- ## Task 6: Visual verification gate (was O-T8) **Goal:** Confirm that the extraction didn't regress rendering by side-by-side with main against retail in three scenes. **Files:** none modified; this is a runtime test. - [ ] **Step 1: Capture pre-extraction screenshots (do this BEFORE Task 2 if not already done)** If not already captured: pause this plan, switch back to `main` (or a commit at HEAD before Task 1), launch acdream at three scenes: - Holtburg town (outdoor + scenery) - An inn interior (EnvCell) - A dungeon (portals) Save screenshots to `docs/research/phase-o-pre/holtburg-town.png`, `inn-interior.png`, `dungeon.png` (not committed; reference only). If you forgot to capture pre-screenshots before starting: you can either roll back to a pre-O commit (`git checkout ` on a scratch branch), or compare visually against the retail client side-by-side instead. - [ ] **Step 2: Launch acdream after Task 5 with the test character** Use the launch script from CLAUDE.md (PowerShell): ```powershell $env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call" $env:ACDREAM_LIVE = "1" $env:ACDREAM_TEST_HOST = "127.0.0.1" $env:ACDREAM_TEST_PORT = "9000" $env:ACDREAM_TEST_USER = "testaccount" $env:ACDREAM_TEST_PASS = "testpassword" dotnet build # must be green first dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 | Tee-Object -FilePath "launch-O-verification.log" ``` Run in the background per CLAUDE.md guidance. - [ ] **Step 3: Visit the three scenes** In the running client: 1. **Holtburg town** — walk around, check terrain blending, scenery placement (trees/bushes), building exteriors. Compare with pre-O screenshot. 2. **Inn interior** — enter the inn at Holtburg, look at EnvCell geometry (walls, floor, fireplace), check that statics render correctly. 3. **Dungeon** — find any portal (Holtburg Town network portal works for spawn-based visuals; for a real dungeon, walk to a dungeon entrance). Check portal visibility, room geometry. User confirms each scene reads identical to pre-O. - [ ] **Step 4: Measure resident memory at radius=4 + 50 entities visible** In the running client: - Set quality preset to Low (radius=4 — see CLAUDE.md "Quality Preset system"). - Wait for streaming to settle. - Open Task Manager (Windows) → Details tab → find `AcDream.App.exe` → Working Set (Memory). - Record the value. Compare to a pre-O baseline (same scene, same settings, on `main` HEAD before Phase O). Expected delta: ≥50 MB reduction (validates that the second `DefaultDatReaderWriter` cache is actually gone). If <50 MB: investigate why. The dat-reader duplication should be ~50-100 MB per `WbMeshAdapter.cs:27` comment. - [ ] **Step 5: Close client gracefully and record findings** Use the graceful close from CLAUDE.md (WM_CLOSE) so ACE's session clears quickly. Write a brief verification note (in chat or in the T8 commit message): - Three scenes: identical / different (describe difference). - Memory delta: X MB reduction. - User confirmation: yes / no. Do NOT proceed to Task 7 if any scene regressed or memory delta is <50 MB. - [ ] **Step 6: Tag the verification commit (or just push to next task)** No code change at T8 — but optionally record the verification artifacts: ```powershell git add docs/research/ # if you committed any post-O screenshots git commit --allow-empty -m @' chore(O-T8): visual verification passed Three scenes side-by-side against pre-O baseline: identical. - Holtburg town (outdoor + scenery): OK - Holtburg inn interior (EnvCell): OK - (portals): OK Memory measurement at radius=4 + 50 entities visible: - Pre-O: MB working set - Post-O: MB working set - Delta: MB reduction (>= 50 MB target) User confirmed identical rendering. Spec: docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md Co-Authored-By: Claude Opus 4.7 (1M context) '@ ``` --- ## Task 7: Ship (was O-T9) **Files:** - Modify: `CLAUDE.md` — rewrite "WB integration cribs" section. - Modify: `docs/architecture/worldbuilder-inventory.md` — update ownership notes. - Modify: `docs/plans/2026-04-11-roadmap.md` — move Phase O from "in flight" to "shipped". - Modify: `docs/plans/2026-05-12-milestones.md` — note Phase O ship if appropriate. - [ ] **Step 1: Rewrite the "WB integration cribs" section in CLAUDE.md** Find the block in `CLAUDE.md` that starts with "**WorldBuilder is acdream's rendering + dat-handling base, integrated as of Phase N.4 ship (2026-05-08).**" and continues through the WB integration cribs (the bullet list of files and patterns). Replace it with text that reflects post-O reality: ```markdown **WorldBuilder code is extracted into acdream's source tree as of Phase O ship (2026-XX-XX).** The pure helpers (texture decode, scenery transforms, terrain math) live in `src/AcDream.Core/Rendering/Wb/`. The GL-touching infrastructure and mesh pipeline live in `src/AcDream.App/Rendering/Wb/`. There is no `` to `WorldBuilder.Shared` or `Chorizite.OpenGLSDLBackend` in any csproj — those projects remain in `references/WorldBuilder/` as read-references only (grep against them when porting NEW pieces). ALL dat reads go through `DatCollection` (Phase O ship: ONE thing touches the DATs). The previous `DefaultDatReaderWriter` second-reader allocation in `WbMeshAdapter` is deleted. [update the file-path cribs below the header to point at the new locations under `src/AcDream.{Core,App}/Rendering/Wb/`] ``` Preserve all of the substantive cribs about GL pipeline behavior, the indirect-draw command struct, the surface-id rules, etc. — just retarget the file paths and remove the "WB is a project reference" language. - [ ] **Step 2: Update `docs/architecture/worldbuilder-inventory.md`** Add a top-of-file banner indicating the inventory now describes "what we extracted vs what we left in references/." Update the §1 status line. The body inventory tables stay valuable (they describe the WB source we might still grep). Add a column or section noting "Extracted into acdream at Phase O" for each component that moved. - [ ] **Step 3: Update the roadmap** In `docs/plans/2026-04-11-roadmap.md`, find Phase O in the "active" / "in flight" section. Move it to "shipped" with a one-line summary: ``` Phase O — DatPath Unification (2026-XX-XX, ): extracted ~33 WB files / ~7.7K LOC into src/AcDream.{Core,App}/Rendering/Wb/. ObjectMeshManager refactored to take DatCollection (). Memory reduction MB. Reference: docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md. ``` - [ ] **Step 4: Update milestones doc** In `docs/plans/2026-05-12-milestones.md`, locate the M1.5 block. Phase O pre-empted M1.5 — add a brief writeup that O has shipped and M1.5 work can resume from the 2026-05-20 baseline. - [ ] **Step 5: Build + test final** Run: ``` dotnet build dotnet test ``` Expected: both green. - [ ] **Step 6: Final commit + Phase O ship marker** ```powershell git add CLAUDE.md docs/architecture/worldbuilder-inventory.md docs/plans/2026-04-11-roadmap.md docs/plans/2026-05-12-milestones.md git commit -m @' ship(O): Phase O — DatPath Unification — SHIPPED ONE thing touches the DATs. WB code lives in our repo: - src/AcDream.Core/Rendering/Wb/ — pure helpers (5 files, ~782 LOC) - src/AcDream.App/Rendering/Wb/ — GL infra + mesh pipeline (~25 files, ~7K LOC) Project references to WorldBuilder.Shared + Chorizite.OpenGLSDLBackend dropped from AcDream.App.csproj and AcDream.Core.csproj. references/ WorldBuilder remains in-tree as read-reference. DefaultDatReaderWriter removed; DatCollection is the only dat reader. Working-set memory delta: MB reduction at radius=4 + 50 entities. Visual side-by-side passed: Holtburg town, inn interior, dungeon. Updates: - CLAUDE.md: rewrote WB integration cribs to point at extracted locations. - docs/architecture/worldbuilder-inventory.md: ownership now ours. - Roadmap: moved Phase O to "shipped". - Milestones: M1.5 resumes from 2026-05-20 baseline. Spec: docs/superpowers/specs/2026-05-21-phase-o-dat-path-unification-design.md Co-Authored-By: Claude Opus 4.7 (1M context) '@ ``` - [ ] **Step 7: Merge to main** Per spec O-D6 (one ship-window). Open a PR (or fast-forward merge if the worktree branch is clean) targeting main. Use `gh pr create` per CLAUDE.md if a PR is desired; or: ```powershell git checkout main git merge claude/serene-leakey-e6b8bc --ff-only git push ``` Verify with: ``` git status git log -10 ``` --- ## Self-review notes Plan reviewed against spec sections 4, 5, 6: **Spec coverage:** - §4.1 (mesh pipeline) → Task 4. ✓ - §4.2 (GL infra) → Task 3. ✓ - §4.3 (stateless helpers) → Task 2. ✓ - §4.4 (NOT extracted) → no task needed (these are explicitly skipped). - §4.5 (drops) → Task 5. ✓ - §5 task breakdown T2/T3/T4/T5/T7/T8/T9 → plan Tasks 1/3/4/2/5/6/7. ✓ - §6 acceptance criteria → checked at Task 5 step 9 + Task 6 step 4 + Task 7. ✓ - §7 risks → mitigations baked into task steps (T4 safety check at step 2; visual screenshots before Task 1 / Task 6 step 1). ✓ **Placeholder scan:** - "[Insert full MIT license body here...]" in Task 1 step 3 — intentional, requires reading the actual LICENSE file at step 4. Acceptable since step 4 explicitly tells the executor to fetch the content. - "exact filenames during closure walk" in Task 3 + Task 4 file-list — intentional honesty since the audit didn't persist the full per-file table. The closure-walk steps cover producing the exact list. - "" in commit message templates — intentional; commit messages parameterize on findings (e.g., ImageSharp reachability) discovered during the task. **Type consistency:** - `_dats` field name preserved across tasks. - `DatCollection` class name verified — see Task 4 step 4b (executor confirms via grep). - Namespace conventions: `AcDream.Core.Rendering.Wb` for Core helpers, `AcDream.App.Rendering.Wb` for App GL/mesh code. Consistent. **Scope check:** Single ship-window per spec O-D6. Plan is one cohesive unit; no decomposition needed.