Skip to content

refactor: decouple world and RHI interface seams - #835

Merged
github-actions[bot] merged 6 commits into
devfrom
refactor/solid-interface-seams
Jul 6, 2026
Merged

refactor: decouple world and RHI interface seams#835
github-actions[bot] merged 6 commits into
devfrom
refactor/solid-interface-seams

Conversation

@MichaelFisher1997

@MichaelFisher1997 MichaelFisher1997 commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Move world render, shadow scene, and LPV world contracts into engine-rhi with graphics/shadows compatibility aliases.
  • Route game-core/game-ui world operations through role interfaces instead of directly calling concrete World for migrated paths.
  • Add a RenderSystem backend dispatcher and let RHI backends provide individual optional sub-interface vtables.
  • Collapse worldgen generator_interface shims and keep concrete generator access in an explicit test-only namespace.

Verification

  • nix develop --command zig fmt src/ modules/engine-rhi/src/ modules/engine-graphics/src/ modules/engine-shadows/src/ modules/game-core/src/ modules/game-ui/src/ modules/world-runtime/src/ modules/world-worldgen/src/ modules/worldgen-overworld/src/
  • nix develop --command zig build
  • nix develop --command zig build test
  • nix develop --command nix build

Closes #796
Closes #797
Closes #798
Closes #799
Closes #800

@github-actions github-actions Bot added documentation Improvements or additions to documentation engine labels Jul 6, 2026
…ce-seams

# Conflicts:
#	src/engine/graphics/lpv_system.zig
#	src/engine/graphics/shadow_scene.zig
#	src/engine/graphics/world_render_view.zig
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

📋 Summary

PR #835 closes #796, #797, #798, #799, #800.

Critical blocker: nix develop --command zig build fails because engine-rhi/src/world_contracts.zig:4 imports engine-lighting, which engine-rhi is not allowed to access. zig build test currently reports 289/289 passing, but the main executable and benchmark do not compile, so the test target is missing this regression.

📌 Review Metadata

🔴 Critical Issues (Must Fix - Blocks Merge)

[CRITICAL] modules/engine-rhi/src/world_contracts.zig:4 - Build failure: engine-rhi imports unavailable engine-lighting
Confidence: High
Description: world_contracts.zig declares ILPVWorld using GpuLight from engine-lighting, but build.zig never adds engine-lighting as an import for the engine-rhi module. This causes zig build to fail for both the main executable and the benchmark.
Impact: PR cannot be compiled or run; merge would break dev.
Suggested Fix: Either add engine_rhi.addImport("engine-lighting", engine_lighting); in build.zig, or move/copy a minimal GpuLight definition into engine-rhi (e.g. rhi_types.zig) to keep engine-rhi free of higher-level lighting dependencies.

⚠️ High Priority Issues (Should Fix)

[HIGH] modules/game-core/src/session.zig:328, modules/game-core/src/session.zig:355-396, modules/game-ui/src/screens/world.zig:246-258 - Issue #796 migration incomplete
Confidence: High
Description: The PR claims to close #796, but several cited call sites still reach directly into concrete *World: session.zig:328 calls self.world.collisionWorld(); spawn helpers call world.getColumnInfo(); game-ui/screens/world.zig calls self.session.world.getStats()/getRenderStats()/getChunkStateCounts()/getLODStats() and reads self.session.world.generator.info.name.
Impact: The concrete World dependency graph is still dragged into game-core/game-ui, leaving the issue unresolved.
Suggested Fix: Route all remaining call sites through the appropriate role interface: add getColumnInfo to IWorldSimulation, add getChunkStateCounts/getGeneratorName to IWorldTelemetry, and use world.interface().simulation().collisionWorld() in session.zig:328.

[HIGH] modules/engine-rhi/src/rhi.zig:1154-1176 - Issue #797 not fully addressed: RHI.VTable remains monolithic
Confidence: High
Description: The PR adds Interfaces and composeVTable, but RHI.VTable still embeds all 14 sub-vtables. A backend that only needs resource creation must still supply every sub-interface.
Impact: The stated goal of allowing individual sub-interface backends (e.g. a headless asset baker) is not achieved, so #797 should not be closed.
Suggested Fix: Either redesign RHI to hold a set of optional interface pointers (e.g. ?IResourceFactory), or keep this PR scoped to vtable construction and remove Closes #797 from the description.

💡 Medium Priority Issues (Nice to Fix)

[MEDIUM] modules/world-runtime/src/world.zig:178-205, modules/world-runtime/src/world.zig:247-275 - Role interfaces downcast to concrete *World
Confidence: Medium
Description: IWorldSimulation and IWorldTelemetry methods cast self.world.ptr back to *World rather than delegating through the already-defined IWorld vtable.
Impact: The role interfaces are coupled to the concrete World type, weakening the abstraction and making them unsafe for any non-World implementation.
Suggested Fix: Implement the role methods by calling self.world's vtable functions (e.g. self.world.getStats()), and add any missing methods to IWorld.VTable instead of downcasting.

[MEDIUM] modules/game-core/src/session.zig:319-320 - world_sim recreated for pause check
Confidence: Low
Description: A world_sim variable is created at line 301 for player update/interaction, but lines 319-320 construct a fresh self.world.interface().simulation() for the pause branch.
Impact: Minor inconsistency and extra struct copies; no functional bug.
Suggested Fix: Reuse the existing world_sim binding in the else if branch.

ℹ️ Low Priority Suggestions (Optional)

None identified.

📊 SOLID Principles Score

Principle Score Notes
Single Responsibility 7 Role interfaces and backend dispatcher clarify responsibilities, but IWorldSimulation mixes save, physics, block mutation, and pause concerns.
Open/Closed 6 Dispatcher helps for backends, but monolithic RHI.VTable still requires backend edits to add sub-interfaces.
Liskov Substitution 8 Interface structs are uniform; no type-punning issues found.
Interface Segregation 5 World roles are split, but IWorldSimulation is a grab-bag and RHI vtable is not segregated.
Dependency Inversion 5 Game code now depends on abstractions, but the abstractions leak back to concrete *World and engine-rhi now depends on engine-lighting.
Average 6.2

🎯 Final Assessment

Overall Confidence Score: 45%

Confidence Breakdown:

  • Code Quality: 60% (good refactor direction, but incomplete and build-breaking)
  • Completeness: 45% (two of five linked issues are only partially satisfied)
  • Risk Level: 75% (main build fails; incomplete abstraction migration)
  • Test Coverage: 35% (zig build test passes while zig build fails, masking the regression)

Merge Readiness:

  • All critical issues resolved
  • SOLID average score >= 6.0
  • Overall confidence >= 60%
  • No security concerns
  • Tests present and passing (if applicable)

Verdict:

DO NOT MERGE — the PR breaks the main build and does not fully satisfy the linked #796 and #797 requirements.

{
  "reviewed_sha": "019415057bc8071621b02647dd3e3112be6e5693",
  "critical_issues": 1,
  "high_priority_issues": 2,
  "medium_priority_issues": 2,
  "overall_confidence_score": 45,
  "recommendation": "DO NOT MERGE"
}

New%20session%20-%202026-07-06T01%3A05%3A02.222Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

📋 Summary

PR #835 claims to close #796, #797, #798, #799, and #800.

The refactor is directionally correct, builds, and tests pass, but it overclaims closure on two linked issues.

📌 Review Metadata

🔴 Critical Issues (Must Fix - Blocks Merge)

[FIXED] Previous issue: engine-rhi importing unavailable engine-lighting in world_contracts.zig caused zig build to fail.

None identified.

⚠️ High Priority Issues (Should Fix)

[HIGH] modules/game-core/src/session.zig:328, modules/game-core/src/session.zig:353-482, modules/game-ui/src/screens/world.zig:246-263, modules/game-ui/src/screens/world.zig:324-904 - Issue #796 migration incomplete
Confidence: High
Description: #796 requires migrating all game-core/game-ui call sites off concrete *World. Remaining direct usages include: session.zig:328 calls self.world.collisionWorld(); spawn helpers (findSpawnColumn, findActualSpawnColumn, isSpawnPatchStable, checkSpawnArea, findActualSurfaceY) take *World and call world.getColumnInfo() / world.getBlock(); game-ui/screens/world.zig still reads self.session.world.getStats(), getRenderStats(), getChunkStateCounts(), getLODStats(), generator.info.name, render_distance, lod_enabled, lod, renderer, etc.
Impact: Concrete World dependency remains in game-core/game-ui; #796 is not fully satisfied.
Suggested Fix: Add getColumnInfo to IWorldSimulation, route collisionWorld() through self.world.interface().simulation().collisionWorld(), and route all remaining game-ui diagnostic/settings reads through IWorldTelemetry (extend it with getGeneratorName, getChunkStateCounts, isStartupBusy, etc. as needed).

[HIGH] modules/engine-rhi/src/rhi.zig:1154-1176 - Issue #797 not fully addressed: RHI.VTable remains monolithic
Confidence: High
Description: The PR splits Vulkan vtable construction into named sub-tables and adds composeVTable, but RHI.VTable still embeds all 14 sub-vtables. A backend cannot implement only IResourceFactory; it must supply the full composite.
Impact: The stated goal of allowing individual sub-interface backends (e.g. a headless asset baker) is not achieved, so #797 should not be closed.
Suggested Fix: Either redesign RHI to hold optional interface pointers (e.g. resources: ?IResourceFactory), or remove Closes #797 from the PR description and keep this PR scoped to construction cleanup.

💡 Medium Priority Issues (Nice to Fix)

[MEDIUM] modules/world-runtime/src/world.zig:177-205, modules/world-runtime/src/world.zig:247-275 - Role interfaces downcast to concrete *World
Confidence: Medium
Description: IWorldSimulation and IWorldTelemetry methods cast self.world.ptr back to *World rather than delegating through the IWorld vtable.
Impact: The role interfaces are coupled to the concrete World type, weakening the abstraction and making them unsafe for any non-World implementation.
Suggested Fix: Implement role methods via self.world.*() calls, adding any missing methods to IWorld.VTable instead of downcasting.

[MEDIUM] modules/game-core/src/session.zig:319-320 - world_sim binding ignored in pause branch
Confidence: Low
Description: A world_sim variable is created at line 301 for player update/interaction, but lines 319-320 construct a fresh self.world.interface().simulation() for the pause check.
Impact: Minor inconsistency and extra struct copies; no functional bug.
Suggested Fix: Reuse the existing world_sim binding in the else if branch.

ℹ️ Low Priority Suggestions (Optional)

[LOW] modules/world-runtime/src/world.zig:208 vs modules/engine-rhi/src/world_contracts.zig:23 - Name collision between role wrapper and contract
Confidence: Medium
Description: world-runtime defines IWorldRenderView as a role wrapper around IWorld, while engine-rhi defines IWorldRenderView as the vtable contract. The same identifier for two different abstractions is confusing and already forces the local alias GraphicsWorldRenderView.
Impact: Maintainability/readability.
Suggested Fix: Rename the world-runtime role wrapper to WorldRenderViewRole or IWorldRenderFacade.

📊 SOLID Principles Score

Principle Score Notes
Single Responsibility 7 Role interfaces and dispatcher clarify responsibilities, but IWorldSimulation mixes save, physics, block mutation, and pause concerns.
Open/Closed 6 Dispatcher helps for backends, but monolithic RHI.VTable still requires backend edits to add sub-interfaces.
Liskov Substitution 8 Interface structs are uniform; no type-punning issues found.
Interface Segregation 5 World roles are split, but IWorldSimulation is a grab-bag and RHI vtable is not segregated.
Dependency Inversion 5 Game code now depends on abstractions, but the abstractions leak back to concrete *World.
Average 6.2

🎯 Final Assessment

Overall Confidence Score: 60%

Confidence Breakdown:

Merge Readiness:

  • All critical issues resolved
  • SOLID average score >= 6.0
  • Overall confidence >= 60% (at threshold, but unresolved high issues remain)
  • No security concerns
  • Tests present and passing

Verdict:

DO NOT MERGE — the build-breaking critical issue is fixed, but the PR still claims closure of #796 and #797 without fully satisfying their requirements.

{
  "reviewed_sha": "32efbaef81a0a3fa97c441a3a839184cf2fec977",
  "critical_issues": 0,
  "high_priority_issues": 2,
  "medium_priority_issues": 2,
  "overall_confidence_score": 60,
  "recommendation": "DO NOT MERGE"
}

New%20session%20-%202026-07-06T01%3A15%3A05.315Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

📋 Summary

PR #835 claims to close #796, #797, #798, #799, and #800.

The refactor now builds cleanly (debug, test, and ReleaseFast), passes formatting, and zig build test succeeds.

📌 Review Metadata

🔴 Critical Issues (Must Fix - Blocks Merge)

[FIXED] Previous issue: engine-rhi importing unavailable engine-lighting in world_contracts.zig caused zig build to fail.

None identified.

⚠️ High Priority Issues (Should Fix)

[HIGH] modules/engine-rhi/src/rhi.zig:1154-1176 - Issue #797 not fully addressed: RHI.VTable remains monolithic
Confidence: High
Description: #797 explicitly requires dropping composite bundling so backends can implement individual sub-interfaces. The PR adds RHI.Interfaces and composeVTable, but RHI.VTable still embeds all 14 sub-vtables. A headless asset-baking backend would still be forced to supply every sub-interface.
Impact: The stated goal of #797 is not achieved, so the issue should not be closed.
Suggested Fix: Either redesign RHI to hold optional interface pointers (e.g. resources: ?IResourceFactory), or remove Closes #797 from the PR description and keep this PR scoped to construction cleanup.

💡 Medium Priority Issues (Nice to Fix)

[FIXED] Previous issue: IWorldSimulation/IWorldTelemetry methods downcast self.world.ptr to concrete *World in modules/world-runtime/src/world.zig.

None identified.

ℹ️ Low Priority Suggestions (Optional)

[LOW] modules/world-runtime/src/world.zig:325 vs modules/engine-rhi/src/world_contracts.zig:23 - Name collision between role wrapper and contract
Confidence: Medium
Description: world-runtime defines IWorldRenderView as a role wrapper around IWorld, while engine-rhi defines IWorldRenderView as the vtable contract. The world-runtime module already aliases the contract locally as GraphicsWorldRenderView, indicating the collision is confusing.
Impact: Maintainability/readability; future readers may conflate the two abstractions.
Suggested Fix: Rename the world-runtime role wrapper to WorldRenderViewRole or IWorldRenderFacade.

📊 SOLID Principles Score

Principle Score Notes
Single Responsibility 7 Role interfaces and dispatcher clarify responsibilities, but IWorldSimulation still mixes save, physics, block mutation, and pause concerns.
Open/Closed 6 Dispatcher helps for backends, but monolithic RHI.VTable still requires backend edits to add sub-interfaces.
Liskov Substitution 8 Interface structs are uniform; role wrappers now delegate through IWorld rather than downcasting.
Interface Segregation 5 World roles are split, but IWorldSimulation is broad and RHI vtable is not segregated.
Dependency Inversion 7 Game code now depends on abstractions; role wrappers no longer leak back to concrete *World.
Average 6.6

🎯 Final Assessment

Overall Confidence Score: 75%

Confidence Breakdown:

Merge Readiness:

  • All critical issues resolved
  • SOLID average score >= 6.0
  • Overall confidence >= 60% (threshold met, but unresolved high issue remains)
  • No security concerns
  • Tests present and passing

Verdict:

MERGE WITH FIXES — the build-breaking critical issue and the #796 migration are resolved, but the PR still claims closure of #797 without fully satisfying its requirement.

{
  "reviewed_sha": "93923a2f81ff0e8166086cb9c634f3fe7d51fac1",
  "critical_issues": 0,
  "high_priority_issues": 1,
  "medium_priority_issues": 0,
  "overall_confidence_score": 75,
  "recommendation": "MERGE WITH FIXES"
}

New%20session%20-%202026-07-06T01%3A35%3A29.718Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

📋 Summary

PR #835 claims to close #796, #797, #798, #799, and #800.

The refactor builds cleanly in debug, ReleaseFast, zig build test, and nix build, and formatting checks pass.

📌 Review Metadata

🔴 Critical Issues (Must Fix - Blocks Merge)

[FIXED] Previous issue: engine-rhi importing unavailable engine-lighting in world_contracts.zig caused zig build to fail.

None identified.

⚠️ High Priority Issues (Should Fix)

[FIXED] Previous issue: RHI.VTable remained monolithic and forced backends to implement all sub-vtables.

None identified.

💡 Medium Priority Issues (Nice to Fix)

[MEDIUM] modules/game-core/src/session.zig:243 - Issue #796 migration incomplete
Confidence: Medium
Description: GameSession.deinit calls self.world.deinit() directly on the concrete *World field instead of routing through the IWorld interface (self.world.interface().deinit() is available).
Impact: One remaining concrete World call site remains in game-core, so #796 is not fully satisfied despite the PR claiming closure.
Suggested Fix: Change self.world.deinit(); to self.world.interface().deinit();.

ℹ️ Low Priority Suggestions (Optional)

[LOW] modules/world-runtime/src/world.zig:325 vs modules/engine-rhi/src/world_contracts.zig:23 - IWorldRenderView name collision
Confidence: Medium
Description: world-runtime defines IWorldRenderView as a role wrapper around IWorld, while engine-rhi defines IWorldRenderView as the vtable contract. The local GraphicsWorldRenderView alias in world-runtime already signals the collision is confusing.
Impact: Maintainability/readability; future readers may conflate the two abstractions.
Suggested Fix: Rename the world-runtime role wrapper to WorldRenderViewRole or IWorldRenderFacade.

[LOW] modules/engine-rhi/src/rhi.zig:1229-1315 - Partial backend accessors panic on missing sub-interfaces
Confidence: Medium
Description: Every RHI accessor (e.g. factory(), context(), shadow()) uses orelse unreachable. A partial backend that omits an interface will trigger a panic (or UB in ReleaseFast) if that interface is invoked.
Impact: Partial backends are now possible, but misuse is not safely discoverable.
Suggested Fix: Consider adding hasXxx() predicates for optional interfaces, or return optional interfaces / errors from accessors rather than asserting with unreachable.

📊 SOLID Principles Score

Principle Score Notes
Single Responsibility 7 Role interfaces and backend dispatcher clarify responsibilities, but IWorldSimulation still mixes save, physics, block mutation, and pause concerns.
Open/Closed 8 Backends can now provide individual sub-interfaces without editing a monolithic vtable.
Liskov Substitution 9 Interface structs are uniform; role wrappers delegate through IWorld rather than downcasting.
Interface Segregation 7 RHI vtable is now segregated via optional sub-interfaces; IWorldSimulation remains broad.
Dependency Inversion 8 Game code depends on abstractions; engine-rhi no longer leaks back to engine-lighting.
Average 7.8

🎯 Final Assessment

Overall Confidence Score: 85%

Confidence Breakdown:

Merge Readiness:

  • All critical issues resolved
  • SOLID average score >= 6.0
  • Overall confidence >= 60%
  • No security concerns
  • Tests present and passing

Verdict:

MERGE WITH FIXES — the build-breaking critical issue, RHI vtable segregation (#797), and the majority of #796 are resolved, but one direct concrete World call site remains in session.zig.

{
  "reviewed_sha": "99d1aab70c2700e090c17efea6d2a9ab3a873bfd",
  "critical_issues": 0,
  "high_priority_issues": 0,
  "medium_priority_issues": 1,
  "overall_confidence_score": 85,
  "recommendation": "MERGE WITH FIXES"
}

New%20session%20-%202026-07-06T02%3A21%3A13.626Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

📋 Summary

PR #835 closes #796, #797, #798, #799, and #800.

The refactor builds cleanly (zig build, zig build test, nix build), formatting checks pass, and the interface mock tests have been extended to cover the new IWorld vtable methods.

📌 Review Metadata

🔴 Critical Issues (Must Fix - Blocks Merge)

[FIXED] Previous issue: engine-rhi importing unavailable engine-lighting in world_contracts.zig caused zig build to fail.

None identified.

⚠️ High Priority Issues (Should Fix)

[FIXED] Previous issue: RHI.VTable remained monolithic and forced backends to implement all sub-vtables.

None identified.

💡 Medium Priority Issues (Nice to Fix)

[FIXED] Previous issue: GameSession.deinit called concrete self.world.deinit() directly instead of routing through the IWorld interface.

None identified.

ℹ️ Low Priority Suggestions (Optional)

[LOW] modules/world-runtime/src/world.zig:325 vs modules/engine-rhi/src/world_contracts.zig:23 - IWorldRenderView name collision
Confidence: Medium
Description: world-runtime defines IWorldRenderView as a role wrapper around IWorld, while engine-rhi defines IWorldRenderView as the raw vtable contract. The world-runtime module already uses the local alias GraphicsWorldRenderView for the engine-rhi contract, signaling the collision is confusing.
Impact: Maintainability; future readers may conflate the role wrapper with the render contract.
Suggested Fix: Rename the world-runtime role wrapper to WorldRenderViewRole or IWorldRenderFacade.

[LOW] modules/engine-rhi/src/rhi.zig:1229-1315, modules/engine-rhi/src/rhi.zig:1328 - Partial backend accessors panic on missing sub-interfaces
Confidence: Medium
Description: Every RHI accessor (e.g. factory(), context(), shadow()) and waitIdle() uses orelse unreachable. A partial backend that omits an interface will trigger a panic (or UB in ReleaseFast) if that interface is invoked.
Impact: Partial backends are now possible structurally, but misuse is not safely discoverable.
Suggested Fix: Add hasXxx() predicates for optional interfaces, or return optional interfaces/errors from accessors rather than asserting with unreachable.

📊 SOLID Principles Score

Principle Score Notes
Single Responsibility 7 Role interfaces and dispatcher clarify responsibilities, but IWorldSimulation still mixes save, physics, block mutation, and pause concerns.
Open/Closed 8 Backends can now provide individual sub-interfaces without editing a monolithic vtable; dispatcher is open for new backends.
Liskov Substitution 9 Interface structs are uniform; role wrappers delegate through IWorld rather than downcasting.
Interface Segregation 7 RHI vtable is now segregated via optional sub-interfaces; IWorldSimulation remains broad.
Dependency Inversion 8 Game code depends on abstractions; engine-rhi no longer leaks back to engine-lighting.
Average 7.8

🎯 Final Assessment

Overall Confidence Score: 90%

Confidence Breakdown:

  • Code Quality: 90% (clean refactor, builds in all tested modes, new mock tests)
  • Completeness: 95% (all five linked issues fully satisfied)
  • Risk Level: 70% (RHI vtable refactor touches core abstraction, but runtime behavior is unchanged and tests pass)
  • Test Coverage: 80% (existing tests pass; mock tests cover vtable dispatch; no tests for partial RHI backends)

Merge Readiness:

  • All critical issues resolved
  • SOLID average score >= 6.0
  • Overall confidence >= 60%
  • No security concerns
  • Tests present and passing

Verdict:

MERGE — the build-breaking issue, RHI vtable segregation, and role-interface migration are all resolved; only minor naming/safety polish remains.

{
  "reviewed_sha": "1a6c51d1ddfc1a3ba1a310b162b96157f0e3aeb1",
  "critical_issues": 0,
  "high_priority_issues": 0,
  "medium_priority_issues": 0,
  "overall_confidence_score": 90,
  "recommendation": "MERGE"
}

New%20session%20-%202026-07-06T02%3A37%3A21.022Z
opencode session  |  github run

@github-actions
github-actions Bot merged commit d561607 into dev Jul 6, 2026
8 checks passed
@MichaelFisher1997
MichaelFisher1997 deleted the refactor/solid-interface-seams branch July 6, 2026 02:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment