acdream/docs/research/2026-05-21-phase-o-t1-wb-audit.md
Erik b9c111b80d docs(O): O-T1 audit shipped + Phase O spec amended
O-T1 audit (REPORT-ONLY) maps acdream's transitive closure on WorldBuilder:
33 files / ~7.7K LOC across Chorizite.OpenGLSDLBackend (28 files) and
WorldBuilder.Shared (5 files). Verdict on O-Q1 (thread-model): SAFE —
adapters run render-thread only; no worker-thread access to WB code.

Spec amendments incorporated via brainstorm:

- O-D7: Refactor ObjectMeshManager to take DatCollection directly (not
  via adapter). T4 safety check — fall back to thin adapter if call-site
  count >20.
- O-D8: Drop LandSurfaceManager, EnvCellRenderManager, PortalRenderManager,
  TerrainRenderManager from the extract list — audit confirmed not reachable
  (we have our own ports or never used them).
- O-D9: Promote 3 internal types in Chorizite to public on extraction
  (EmbeddedResourceReader, TextureFormatExtensions, BufferUsageExtensions).
- O-D10: Strip [MemoryPackable] from TerrainEntry (we don't serialize).
- O-D11: Namespace AcDream.Core.Rendering.Wb.* for extracted code.
- O-D12: Drop ResolveId + [indoor-upload] NULL_RESULT diagnostic block.

Task breakdown: T6 (EnvCell/portal) eliminated; T5 (stateless helpers)
shrinks to 0.5d; T4 (mesh + refactor) grows to 2.5d. Net effort estimate
holds at ~7.75d.

All originally-open spec questions are now closed (Q1/Q2/Q3/Q4) or
deferred to T3 with an explicit verify step (Q5: SixLabors.ImageSharp
reachability).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 14:35:38 +02:00

27 KiB
Raw Blame History

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 T2T7 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 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 ~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 55 TerrainEntry Bridges LandBlockTerrainEntry[81] for WB scenery helpers
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 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 TextureHelpers.Fill* Byte-identity tests vs WB; should stay after extraction (validates our copy matches the original)
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 38-39 <ProjectReference> to WorldBuilder.Shared + Chorizite.OpenGLSDLBackend
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 (T3T6, 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 anticipatedLandSurfaceManager 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 internalpublic 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)
  • MemoryPackTerrainEntry 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).
  • 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)

  • Audit doc exists at the agreed path.
  • Every file in the closure is bucketed (T3 / T4 / T5 / T6 / NOT / REPLACE).
  • Hidden-dependency risks section addresses internal types, source generators, resource files, and DefaultDatReaderWriter vs DatCollection semantic diff.
  • 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.)
  • Thread-model finding included.
  • 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 IDatReaderWriterDatCollection 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.