Goal
Track and drive down SOLID-principle debt across the engine. This is the umbrella issue — an in-depth, evidence-cited audit of ~78,726 LOC across 29 modules. Work lands in 4 priority phases (P0–P3), each carved into sub-issues containing small reviewable PRs targeting dev.
The audit is read-only; this issue is the canonical reference. Every finding below cites a file:line so sub-issues can be scoped precisely.
Scope & method
- 4 parallel deep-dives: RHI/graphics, worldgen, world-runtime/meshing/LOD, cross-cutting (duplication, wiring, dependency direction, concurrency).
- Each principle graded per subsystem. Verdict scale: Strong · Adequate · Weak · Violated.
- 32 findings (5 Critical, 21 Major, 6 Minor) + 9 architectural strengths to preserve.
Scorecard
| Principle |
Overall |
Worst area |
| SRP — Single Responsibility |
Weak |
lod_mesh.zig (2,277 LOC, ~14 responsibilities) |
| OCP — Open/Closed |
Weak |
RHI native-handles seam; hardcoded generator registry |
| LSP — Liskov Substitution |
Adequate |
bindShader silent no-op; INativeHandlesContext returns 0 from Mock |
| ISP — Interface Segregation |
Adequate |
Dead segregated worldgen interfaces; fat IRenderOptionsContext |
| DIP — Dependency Inversion |
Weak |
gpu_mesher.zig downcasts rhi.ptr → *VulkanContext |
Critical findings
C1 — 16,043 lines of dead code: stale Vulkan fork
Where: src/engine/graphics/vulkan/*.zig (57 files)
Evidence: build.zig:126 is the only graphics root and points at modules/engine-graphics/src/root.zig. Nothing imports src/engine/. The only diffs vs the live copy are import-path rewrites (../rhi.zig → engine-rhi). Compiles nominally, never reached, never tested, silently rots.
Fix: Delete src/engine/graphics/vulkan/ entirely. Zero behavior change.
C2 — INativeHandlesContext leaks Vulkan through the abstraction
Where: modules/engine-rhi/src/rhi.zig:763-830; callers modules/engine-graphics/src/atmosphere_system.zig:39-42, render_graph.zig:645-655
Evidence: 15 methods like getSkyPipeline() u64 return Vulkan handles disguised as u64. Callers immediately cast back to VkPipeline/VkCommandBuffer and issue raw vkCmdBindPipeline/vkCmdBindDescriptorSets. A non-Vulkan backend cannot return a VkPipeline — OCP is violated for sky, water, debug-shadow.
Fix: Move sky/water/shadow draws behind the RHI as first-class methods (drawSky(params), drawWater(...)). Eliminate INativeHandlesContext.
C3 — gpu_mesher.zig bypasses the RHI and downcasts to VulkanContext
Where: modules/world-runtime/src/gpu_mesher.zig:9-11,89; same pattern in modules/engine-graphics/src/lpv_backend.zig:13-15
Evidence:
const graphics = @import("engine-graphics");
const VulkanContext = graphics.VulkanContext;
const vk_ctx: *VulkanContext = @ptrCast(@alignCast(rhi.ptr)); // line 89
// ...then 27 raw c.vk* calls (vkCmdDispatch, vkCmdPipelineBarrier, vkWaitForFences...)
The highest-tier module (world-runtime) imports the concrete Vulkan backend and reaches into private frames/resources. lpv_backend.zig:6-7 candidly documents this as intentional.
Fix: Add a compute facet to RHI.VTable (createComputePipeline, dispatch, pipelineBarrier, pushConstants, fillBuffer). Rewrite gpu_mesher + LPV against it. Remove both @ptrCast downcasts.
C4 — lod_mesh.zig is a 2,277-line god-file (~14 responsibilities)
Where: modules/world-lod/src/lod_mesh.zig
Evidence: One file mixes: RHI buffer vtable (43-126), 4 mesh-build algorithms (193-544), skirt geometry (569-728), column-span math (731-900), UV mapping (901-922), material heuristics (932-1040), vegetation impostors (1041-1407), color sampling (1122-1172), vertex packing (1198-1234), quad emission (1236-1334), height stitching (1077-1120), LOD orchestration (1410-1588), and 686 lines of embedded tests (18 test blocks, 1608-2277).
Fix: Split into lod_mesh_resources.zig, lod_mesh.zig, lod_geometry.zig, lod_materials.zig, lod_vegetation.zig; move tests to lod_mesh_tests.zig.
C5 — Worldgen segregated interfaces are dead code
Where: modules/worldgen-api/src/root.zig:139-177 (IChunkGenerator, ILODHeightmapGenerator, IGeneratorInfoProvider, ICacheRecenterable)
Evidence: No consumer uses them. The registry demands the full fat 7-method Generator, forcing FlatWorldGenerator and ShadowTestWorldGenerator to publish no-op shims (worldgen-flat/src/root.zig:68-90, worldgen-test/src/root.zig:166-187) for methods semantically meaningless to them.
Fix: Either consume the segregated interfaces at registry/runtime call-sites (split registration into "chunk-only" vs "chunk+LOD"), or delete them.
Major findings
Single Responsibility (SRP)
- M1 —
modules/worldgen-overworld-v2/src/root.zig (1,479 LOC) inlines 9 concerns: noise math, climate, terrain shape, biome select, caves, trees, vegetation, LOD sampling, block/color. Regression vs the well-decomposed v1. Fix: split along v1's collaborator shape.
- M2 —
modules/worldgen-overworld/src/overworld_generator.zig (1,107 LOC) inlines LOD tinting (662-725), tree-hint computation (492-621), material layering (884-931). Fix: extract lod_coloring.zig, tree_hints.zig.
- M3 —
modules/engine-rhi/src/rhi.zig (1,384 LOC) mixes vtables + 4 wrappers + legacy composite + ~50 deprecated passthroughs (self-admitted at line 1012). VulkanContext (rhi_context_types.zig:192-231) is a 24+-field god-struct spanning every subsystem. Fix: split into interfaces.zig + wrappers.zig; delete passthroughs; group VulkanContext fields.
- M4 —
modules/world-runtime/src/world_renderer.zig:487-653 interleaves ~180 lines of diagnostic block-type logging inside the CPU cull loop. Fix: extract world_diagnostics.zig.
- M5 —
modules/world-runtime/src/world_mutation.zig:53-92, 137-422 embeds a full BFS lighting engine (~285 lines) inside the block-edit coordinator. Fix: extract LightingEngine.
- M6 —
modules/world-runtime/src/world.zig:717-831 embeds the LPV light-grid builder (~115 lines) in the facade. Fix: extract LpvGridBuilder.
Open/Closed (OCP)
- M7 —
modules/world-meshing/src/chunk_mesh.zig:178-182 hardcodes the mesher sequence (cross/flat-quad/tall-cross/wall/custom). No strategy table; adding a render_shape forces editing this file. Fix: function-pointer table keyed on render_shape.
- M8 —
modules/world-worldgen/src/registry.zig:24-49 hardcodes all 4 generators in GENERATORS. Adding worldgen-foo requires edits to 6 files (build.zig ×2, registry.zig ×2, root.zig, app.zig). Fix: comptime array of *const GeneratorDescriptor.
- M9 —
modules/world-worldgen/src/registry.zig:51-65 redefines a factory signature different from the GeneratorDescriptor.create the API already provides, forcing 4 trivially-delegating wrappers. Fix: consume GeneratorDescriptor directly.
- M10 —
modules/engine-graphics/src/render_system.zig:127-128 hardcodes rhi_vulkan.createRHI; no backend dispatcher. render_device.zig:14 doc-comment advertises a backend_type param the real signature lacks. Fix: BackendChoice enum or registered factory.
Interface Segregation (ISP)
- M11 —
modules/engine-rhi/src/rhi.zig:907-1010 IRenderOptionsContext is a 23-method kitchen sink (quality toggles + device recovery + culling-system factory + frame capture). Fix: split into IRenderQualityOptions / IDeviceRecovery / ICullingSystemFactory / IScreenshotContext.
- M12 —
modules/engine-rhi/src/rhi.zig:1028-1046 composite RHI.VTable re-bundles all 14 sub-vtables (comment: (temp)). A backend must implement all 14 even for resource creation only. rhi_vulkan.zig:959-1067 is one 100+-function literal. Fix: allow backends to implement individual sub-interfaces.
- M13 —
IWorldSimulation/IWorldRenderView/IWorldTelemetry exist and render_graph.zig uses them, but game-core/session.zig:228,320,355, player.zig:277,386, game-ui/screens/world.zig:135,243 reach past them into the concrete 845-line World. Fix: migrate callers onto the role interfaces.
Liskov Substitution (LSP)
- M14 —
modules/engine-rhi/src/rhi_vulkan.zig:764-769 bindShader is a silent no-op in Vulkan but in the public IGraphicsCommandEncoder contract. Undefined behavioral contract.
- M15 —
modules/engine-rhi/src/rhi_tests.zig:84-88 Mock returns 0 from getNativeSkyPipeline, forcing callers to defensively special-case 0 (atmosphere_system.zig:31-37) — textbook LSP smell. Resolved implicitly by C2.
- M16 —
modules/engine-rhi/src/render_device.zig is split-brain: backend_data always null; real IResourceFactory.createBuffer ignores it. Two unrelated types sharing a name.
Dependency Inversion (DIP)
- M17 —
modules/world-worldgen/src/root.zig:53-61 re-exports concrete OverworldGenerator/FlatWorldGenerator/etc.; consumers (src/worldgen_tests.zig:11,19) reach past the registry. DIP boundary unenforced.
- M18 — Triplicated
generator_interface.zig re-export shim in world-worldgen, worldgen-overworld, worldgen-api. Fix: @import("worldgen-api") directly.
- M19 —
modules/world-runtime/src/world.zig:44-45 imports interface types IWorldRenderView/IShadowScene/ILPVWorld from the concrete engine-graphics. Fix: move contracts into engine-rhi.
- M20 —
modules/worldgen-overworld/src/world_map.zig:7-21 a world generator instantiates a GPU Texture via engine-rhi. Worldgen should produce pixels; an upper layer owns the GPU resource.
- M21 — Mutable globals:
modules/engine-ui/src/font.zig:8 var active_atlas (hidden, no sync, read every drawText); modules/game-core/src/settings/json_presets.zig:51 pub var graphics_presets (process-wide list, no mutex). Fix: inject the font atlas; mutex-guard graphics_presets.
Minor / hygiene
- m1 — Stale build deps:
build.zig:265 (world-meshing → engine-graphics) and build.zig:281 (world-worldgen → engine-rhi) have zero @import hits. Remove.
- m2 — ~30 shim re-export files under
src/engine/ kept alive only by legacy relative-path imports in src/game/app.zig:6-33. Switch to package imports and delete the tree.
- m3 —
modules/engine-core/src/job_system.zig:393 var cleanup_count is test-only at file scope. Move into a test block.
- m4 —
modules/worldgen-common/src/lighting_computer.zig:12,53 uses a u8 as a fake anyopaque pointer. Document or restructure.
- m5 —
modules/engine-rhi/src/render_device.zig:14 doc-comment lies about a backend_type parameter. Fix doc or implement.
- m6 —
modules/engine-graphics/src/rhi_vulkan.zig:742 createShader skips the mutex its siblings hold. Document/align the threading contract.
Strengths to preserve (do not regress)
These demonstrate the team executes cleanly — the issues elsewhere are correctable, not structural. Any refactor in the phases below must not break them:
- P1 — Composition root (
src/game/app.zig:119-261) is textbook manual constructor injection with errdefer cleanup. No service locator, no IoC container.
- P2 — Worker-thread RHI isolation is rigorously respected: zero RHI/Vulkan calls on workers; GPU upload on main thread; chunk
pin()/unpin() discipline (chunk_queue_coordinator.zig:326-353).
- P3 — No dependency cycles; clean layered DAG. Engine never imports world/game.
engine-rhi imports only engine-math + engine-core. engine-physics has zero project imports.
- P4 — Mutexes narrowly scoped per-subsystem (SaveManager has 3 separate locks; per-mesh locks; per-device lock). No global lock, no cross-module lock ordering.
- P5 — Worldgen LSP is strong: all 4 generators fully substitutable, no
@panic("unsupported")/unreachable in vtable methods, runtime treats them polymorphically.
- P6 — V1 worldgen (
overworld_generator.zig) is well-decomposed into named collaborators (NoiseSampler, BiomeSource, CaveSystem, etc.).
- P7 — LOD bridge interfaces (
lod_upload_queue.zig LODGPUBridge/LODRenderInterface; lod_mesh.zig:43-126 LODMeshResources) are clean inversion seams — LOD core doesn't import engine-rhi.
- P8 — Meshing leaf modules (
cross_mesher, custom_mesh_mesher, flat_quad_mesher, etc.) are tight, single-shape, SRP-respecting.
- P9 — Vulkan managers (
render_pass_manager.zig, pipeline_manager.zig, descriptor_manager.zig) carry an explicit "Extracted from rhi_vulkan.zig to eliminate the god object anti-pattern" header — SRP is actively being pursued.
Remediation phases
Each phase becomes one or more sub-issues. Phases are ordered by impact and risk; P0 unblocks a second graphics backend, P3 is hygiene that shrinks the surface for the bigger refactors.
P0 — Unblock multi-backend & decruft (Critical)
P1 — Split the god-objects (Major SRP)
P2 — Close OCP/ISP holes (Major OCP/ISP)
P3 — Hygiene (Minor + remaining Major DIP)
Constraints
- Use existing architecture (RHI vtables, job tokens,
pin/unpin, LODGPUBridge, role interfaces) — no parallel systems.
- Small reviewable PRs targeting
dev; conventional commits (refactor:, feat:); never push to dev directly.
- Preserve current gameplay/rendering behavior except where a sub-issue explicitly changes it.
- All build/test commands wrapped in
nix develop --command.
- Verification baseline for every PR:
nix develop --command zig build test (includes shader validation); rendering-touching PRs also run headless crash/screenshot/benchmark skill checks with -Dskip-present.
- P0 work that touches the RHI seam must keep the existing Vulkan path green end-to-end before any second-backend work is considered.
Key risks
- RHI seam refactor (C2/C3) is the highest-risk, highest-value work. Sky/water/LPV/GPU-mesher all bypass the abstraction today; moving them behind the RHI without regressing visuals requires headless screenshot baselines before and after.
lod_mesh.zig split (C4) touches the largest file in the repo with 686 lines of embedded tests — tests must move with their code, not be dropped.
worldgen-overworld-v2 decomposition (M1) must preserve terrain output bit-for-bit; pin a golden-output test before splitting.
- Threading: any refactor near the job system must keep worker-thread RHI isolation (P2) intact — no new ad-hoc locks, no RHI calls off the main thread.
- Phasing dependency: P1 god-object splits should land before P2 interface work where possible, so the new interfaces are designed against already-decomposed collaborators.
Goal
Track and drive down SOLID-principle debt across the engine. This is the umbrella issue — an in-depth, evidence-cited audit of ~78,726 LOC across 29 modules. Work lands in 4 priority phases (P0–P3), each carved into sub-issues containing small reviewable PRs targeting
dev.The audit is read-only; this issue is the canonical reference. Every finding below cites a
file:lineso sub-issues can be scoped precisely.Scope & method
Scorecard
lod_mesh.zig(2,277 LOC, ~14 responsibilities)bindShadersilent no-op;INativeHandlesContextreturns 0 from MockIRenderOptionsContextgpu_mesher.zigdowncastsrhi.ptr→*VulkanContextCritical findings
C1 — 16,043 lines of dead code: stale Vulkan fork
Where:
src/engine/graphics/vulkan/*.zig(57 files)Evidence:
build.zig:126is the only graphics root and points atmodules/engine-graphics/src/root.zig. Nothing importssrc/engine/. The only diffs vs the live copy are import-path rewrites (../rhi.zig→engine-rhi). Compiles nominally, never reached, never tested, silently rots.Fix: Delete
src/engine/graphics/vulkan/entirely. Zero behavior change.C2 —
INativeHandlesContextleaks Vulkan through the abstractionWhere:
modules/engine-rhi/src/rhi.zig:763-830; callersmodules/engine-graphics/src/atmosphere_system.zig:39-42,render_graph.zig:645-655Evidence: 15 methods like
getSkyPipeline() u64return Vulkan handles disguised asu64. Callers immediately cast back toVkPipeline/VkCommandBufferand issue rawvkCmdBindPipeline/vkCmdBindDescriptorSets. A non-Vulkan backend cannot return aVkPipeline— OCP is violated for sky, water, debug-shadow.Fix: Move sky/water/shadow draws behind the RHI as first-class methods (
drawSky(params),drawWater(...)). EliminateINativeHandlesContext.C3 —
gpu_mesher.zigbypasses the RHI and downcasts toVulkanContextWhere:
modules/world-runtime/src/gpu_mesher.zig:9-11,89; same pattern inmodules/engine-graphics/src/lpv_backend.zig:13-15Evidence:
The highest-tier module (
world-runtime) imports the concrete Vulkan backend and reaches into private frames/resources.lpv_backend.zig:6-7candidly documents this as intentional.Fix: Add a compute facet to
RHI.VTable(createComputePipeline,dispatch,pipelineBarrier,pushConstants,fillBuffer). Rewritegpu_mesher+ LPV against it. Remove both@ptrCastdowncasts.C4 —
lod_mesh.zigis a 2,277-line god-file (~14 responsibilities)Where:
modules/world-lod/src/lod_mesh.zigEvidence: One file mixes: RHI buffer vtable (43-126), 4 mesh-build algorithms (193-544), skirt geometry (569-728), column-span math (731-900), UV mapping (901-922), material heuristics (932-1040), vegetation impostors (1041-1407), color sampling (1122-1172), vertex packing (1198-1234), quad emission (1236-1334), height stitching (1077-1120), LOD orchestration (1410-1588), and 686 lines of embedded tests (18
testblocks, 1608-2277).Fix: Split into
lod_mesh_resources.zig,lod_mesh.zig,lod_geometry.zig,lod_materials.zig,lod_vegetation.zig; move tests tolod_mesh_tests.zig.C5 — Worldgen segregated interfaces are dead code
Where:
modules/worldgen-api/src/root.zig:139-177(IChunkGenerator,ILODHeightmapGenerator,IGeneratorInfoProvider,ICacheRecenterable)Evidence: No consumer uses them. The registry demands the full fat 7-method
Generator, forcingFlatWorldGeneratorandShadowTestWorldGeneratorto publish no-op shims (worldgen-flat/src/root.zig:68-90,worldgen-test/src/root.zig:166-187) for methods semantically meaningless to them.Fix: Either consume the segregated interfaces at registry/runtime call-sites (split registration into "chunk-only" vs "chunk+LOD"), or delete them.
Major findings
Single Responsibility (SRP)
modules/worldgen-overworld-v2/src/root.zig(1,479 LOC) inlines 9 concerns: noise math, climate, terrain shape, biome select, caves, trees, vegetation, LOD sampling, block/color. Regression vs the well-decomposed v1. Fix: split along v1's collaborator shape.modules/worldgen-overworld/src/overworld_generator.zig(1,107 LOC) inlines LOD tinting (662-725), tree-hint computation (492-621), material layering (884-931). Fix: extractlod_coloring.zig,tree_hints.zig.modules/engine-rhi/src/rhi.zig(1,384 LOC) mixes vtables + 4 wrappers + legacy composite + ~50 deprecated passthroughs (self-admitted at line 1012).VulkanContext(rhi_context_types.zig:192-231) is a 24+-field god-struct spanning every subsystem. Fix: split intointerfaces.zig+wrappers.zig; delete passthroughs; groupVulkanContextfields.modules/world-runtime/src/world_renderer.zig:487-653interleaves ~180 lines of diagnostic block-type logging inside the CPU cull loop. Fix: extractworld_diagnostics.zig.modules/world-runtime/src/world_mutation.zig:53-92, 137-422embeds a full BFS lighting engine (~285 lines) inside the block-edit coordinator. Fix: extractLightingEngine.modules/world-runtime/src/world.zig:717-831embeds the LPV light-grid builder (~115 lines) in the facade. Fix: extractLpvGridBuilder.Open/Closed (OCP)
modules/world-meshing/src/chunk_mesh.zig:178-182hardcodes the mesher sequence (cross/flat-quad/tall-cross/wall/custom). No strategy table; adding arender_shapeforces editing this file. Fix: function-pointer table keyed onrender_shape.modules/world-worldgen/src/registry.zig:24-49hardcodes all 4 generators inGENERATORS. Addingworldgen-foorequires edits to 6 files (build.zig ×2, registry.zig ×2, root.zig, app.zig). Fix: comptime array of*const GeneratorDescriptor.modules/world-worldgen/src/registry.zig:51-65redefines a factory signature different from theGeneratorDescriptor.createthe API already provides, forcing 4 trivially-delegating wrappers. Fix: consumeGeneratorDescriptordirectly.modules/engine-graphics/src/render_system.zig:127-128hardcodesrhi_vulkan.createRHI; no backend dispatcher.render_device.zig:14doc-comment advertises abackend_typeparam the real signature lacks. Fix:BackendChoiceenum or registered factory.Interface Segregation (ISP)
modules/engine-rhi/src/rhi.zig:907-1010IRenderOptionsContextis a 23-method kitchen sink (quality toggles + device recovery + culling-system factory + frame capture). Fix: split intoIRenderQualityOptions/IDeviceRecovery/ICullingSystemFactory/IScreenshotContext.modules/engine-rhi/src/rhi.zig:1028-1046compositeRHI.VTablere-bundles all 14 sub-vtables (comment:(temp)). A backend must implement all 14 even for resource creation only.rhi_vulkan.zig:959-1067is one 100+-function literal. Fix: allow backends to implement individual sub-interfaces.IWorldSimulation/IWorldRenderView/IWorldTelemetryexist andrender_graph.ziguses them, butgame-core/session.zig:228,320,355,player.zig:277,386,game-ui/screens/world.zig:135,243reach past them into the concrete 845-lineWorld. Fix: migrate callers onto the role interfaces.Liskov Substitution (LSP)
modules/engine-rhi/src/rhi_vulkan.zig:764-769bindShaderis a silent no-op in Vulkan but in the publicIGraphicsCommandEncodercontract. Undefined behavioral contract.modules/engine-rhi/src/rhi_tests.zig:84-88Mock returns 0 fromgetNativeSkyPipeline, forcing callers to defensively special-case 0 (atmosphere_system.zig:31-37) — textbook LSP smell. Resolved implicitly by C2.modules/engine-rhi/src/render_device.zigis split-brain:backend_dataalwaysnull; realIResourceFactory.createBufferignores it. Two unrelated types sharing a name.Dependency Inversion (DIP)
modules/world-worldgen/src/root.zig:53-61re-exports concreteOverworldGenerator/FlatWorldGenerator/etc.; consumers (src/worldgen_tests.zig:11,19) reach past the registry. DIP boundary unenforced.generator_interface.zigre-export shim inworld-worldgen,worldgen-overworld,worldgen-api. Fix:@import("worldgen-api")directly.modules/world-runtime/src/world.zig:44-45imports interface typesIWorldRenderView/IShadowScene/ILPVWorldfrom the concreteengine-graphics. Fix: move contracts intoengine-rhi.modules/worldgen-overworld/src/world_map.zig:7-21a world generator instantiates a GPUTextureviaengine-rhi. Worldgen should produce pixels; an upper layer owns the GPU resource.modules/engine-ui/src/font.zig:8var active_atlas(hidden, no sync, read everydrawText);modules/game-core/src/settings/json_presets.zig:51pub var graphics_presets(process-wide list, no mutex). Fix: inject the font atlas; mutex-guardgraphics_presets.Minor / hygiene
build.zig:265(world-meshing → engine-graphics) andbuild.zig:281(world-worldgen → engine-rhi) have zero@importhits. Remove.src/engine/kept alive only by legacy relative-path imports insrc/game/app.zig:6-33. Switch to package imports and delete the tree.modules/engine-core/src/job_system.zig:393var cleanup_countis test-only at file scope. Move into atestblock.modules/worldgen-common/src/lighting_computer.zig:12,53uses au8as a fakeanyopaquepointer. Document or restructure.modules/engine-rhi/src/render_device.zig:14doc-comment lies about abackend_typeparameter. Fix doc or implement.modules/engine-graphics/src/rhi_vulkan.zig:742createShaderskips the mutex its siblings hold. Document/align the threading contract.Strengths to preserve (do not regress)
These demonstrate the team executes cleanly — the issues elsewhere are correctable, not structural. Any refactor in the phases below must not break them:
src/game/app.zig:119-261) is textbook manual constructor injection witherrdefercleanup. No service locator, no IoC container.pin()/unpin()discipline (chunk_queue_coordinator.zig:326-353).engine-rhiimports onlyengine-math+engine-core.engine-physicshas zero project imports.@panic("unsupported")/unreachablein vtable methods, runtime treats them polymorphically.overworld_generator.zig) is well-decomposed into named collaborators (NoiseSampler,BiomeSource,CaveSystem, etc.).lod_upload_queue.zigLODGPUBridge/LODRenderInterface;lod_mesh.zig:43-126LODMeshResources) are clean inversion seams — LOD core doesn't importengine-rhi.cross_mesher,custom_mesh_mesher,flat_quad_mesher, etc.) are tight, single-shape, SRP-respecting.render_pass_manager.zig,pipeline_manager.zig,descriptor_manager.zig) carry an explicit "Extracted from rhi_vulkan.zig to eliminate the god object anti-pattern" header — SRP is actively being pursued.Remediation phases
Each phase becomes one or more sub-issues. Phases are ordered by impact and risk; P0 unblocks a second graphics backend, P3 is hygiene that shrinks the surface for the bigger refactors.
P0 — Unblock multi-backend & decruft (Critical)
RHI.VTable; rewritegpu_mesher+lpv_backendINativeHandlesContext; move sky/water/shadow draws behind the RHIP1 — Split the god-objects (Major SRP)
lod_mesh.ziginto 5 focused modules + test fileworldgen-overworld-v2/root.zigalong v1 shaperhi.zigintointerfaces.zig+wrappers.zig; delete deprecated passthroughs; groupVulkanContextfieldsLightingEnginefromworld_mutation.zigLpvGridBuilderfromworld.zigworld_renderercull loopoverworld_generator.zigP2 — Close OCP/ISP holes (Major OCP/ISP)
[*]const *const GeneratorDescriptor; delete wrapper boilerplateIRenderOptionsContextinto 4 role interfacesRenderSystem.initP3 — Hygiene (Minor + remaining Major DIP)
IWorldRenderView/IShadowScene/ILPVWorldintoengine-rhigenerator_interfaceshimsaddImportlines; switchapp.zigto package imports; deletesrc/engine/treeRenderDevice; fixbindShader/doc liesgraphics_presetsConstraints
pin/unpin,LODGPUBridge, role interfaces) — no parallel systems.dev; conventional commits (refactor:,feat:); never push todevdirectly.nix develop --command.nix develop --command zig build test(includes shader validation); rendering-touching PRs also run headless crash/screenshot/benchmark skill checks with-Dskip-present.Key risks
lod_mesh.zigsplit (C4) touches the largest file in the repo with 686 lines of embedded tests — tests must move with their code, not be dropped.worldgen-overworld-v2decomposition (M1) must preserve terrain output bit-for-bit; pin a golden-output test before splitting.