Skip to content

Codebase Observations

github-actions[bot] edited this page Aug 23, 2026 · 1 revision

Codebase Observations — Non-Obvious Facts

Audience: Programmers | Mixed

Thread Context: N/A (audit/reference)

Platform/Backend Scope: All platforms / all backends

Overview

Non-obvious facts about SparkEngine's codebase structure, conventions, and tooling that are easy to get wrong. These are things not immediately apparent from reading CLAUDE.md or the rest of the wiki, or things that contradict common assumptions. Check these before making assumptions about how the engine builds, where subsystems live, or which files are safe to edit by hand.


Most Optional Systems Are OFF by Default — but Networking Is Now ON

Several major engine systems are gated behind CMake toggles. A build can succeed even if code in a disabled system is broken, because that code is never compiled.

Current default toggle state (verify with cmake --preset linux-gcc-release -N):

ENABLE_NETWORKING=ON    ← now ON by default (was OFF at the time of the original observation)
ENABLE_DXR=ON           ← on by default (SDFGI fallback when no D3D12/DXR hardware)
ENABLE_VR=OFF           ← VR is implemented and wired in, but disabled by default
ENABLE_METAL=OFF        ← experimental, off by default

The original observation flagged networking as the most common trap because it was off by default. That has since changed — ENABLE_NETWORKING is ON by default today, so networking code now compiles in standard CI builds. The general lesson still holds: if code in a disabled system silently breaks, it won't show up in CI; and if a subsystem "doesn't exist," check the CMake toggle first.


Use EngineContext, Not Global Variables

Legacy free-floating globals (g_graphics, g_input, g_timer, g_eventBus, g_audioEngine, g_audioBackend, g_moduleManager, g_moduleHotReload, g_physicsOwned) have been removed. Engine-lifetime ownership now lives in an EngineRuntime struct (SparkEngine/Source/Core/EngineRuntime.h), accessed via GetEngineRuntime(), intended for Core/ entry-point and lifecycle files only.

All other code accesses subsystems through the EngineContext service locator:

// WRONG — removed globals (no longer declared anywhere):
g_graphics->RenderFrame();

// CORRECT — EngineContext service locator:
auto* graphics = EngineContext::Get()->GetGraphics();
auto* input    = EngineContext::Get()->GetInput();
  • EngineContext lives in SparkEngine/Source/Core/EngineContext.h.
  • EngineRuntime (private ownership container) lives in SparkEngine/Source/Core/EngineRuntime.h.
  • Do not introduce new file-scope g_* subsystem globals.

.claude/ Is Intentionally Visible to Claude

.claude/ is NOT excluded from .promptignore — Claude reads these files at session start to load persistent context. Do not add .claude/ to .promptignore.

.promptignore excludes ThirdParty/, Shaders/Compiled/, build/, IDE config dirs, .git/, binary files, and generated docs/html/ + docs/xml/. Everything else (including .claude/) is visible.


docs/api/ Files Are Entirely Auto-Generated

Every file under docs/api/ is generated by docs/generate-api-docs.sh. Never edit them by hand — changes are overwritten on the next run. To change API documentation, edit the Doxygen comments (@brief, @param, @return) in the header files under SparkEngine/Source/; the generation script extracts from there.

The script uses checksum-based caching (docs/generate-api-docs.sh check) and only regenerates pages whose headers changed. Note: docs/api/ is a generated artifact and may be absent from a fresh checkout until the script runs.

The same hands-off rule applies to <!-- AUTO:* --> sections in wiki files — they are written by docs/sync-wiki.sh sync.


Wiki Files Mix Auto and Manual Content

Wiki pages are mostly hand-written, but specific sections are auto-generated and fenced by markers:

<!-- AUTO:component-list -->
... generated content, do not edit ...
<!-- /AUTO:component-list -->
  • Outside <!-- AUTO:* --> blocks: edit freely.
  • Inside them: do not edit — docs/sync-wiki.sh sync will overwrite.
  • During rebase conflicts in wiki files, take upstream for AUTO: sections.
  • To add a new page: create the file in wiki/, then add it to wiki/_Sidebar.md.

Two tools/ Directories with Different Purposes

Directory Contents Purpose
tools/ (lowercase) validate-prompts.sh, check-*.sh, asset generators, wine-run.sh Validation and dev scripts
Tools/ (uppercase) spark-cli/spark_cli.py Developer CLI utility

CI references tools/validate-prompts.sh --ci — always lowercase. On case-insensitive filesystems (macOS) these may appear merged; they are distinct on Linux/CI.


CLAUDE.md Pre-Commit Step 1 Is a Partial Check

The pre-commit format step uses head -50, checking only the first 50 files by modification time. CI's check-format checks all files (including .hpp). It is possible to pass the local shortcut and fail CI. Run the full CI-matching command (no head -50) before pushing.


ThirdParty Is Git Submodules Plus Vendored Snapshots

ThirdParty/ mixes git submodules and vendored single-header/snapshot code. After cloning, initialize submodules before CMake configure:

git submodule update --init --recursive

If a ThirdParty/ directory looks empty or CMake reports missing files there, an uninitialized submodule is almost always the cause. There is now a single source of truth at ThirdParty/dependencies.lock plus a configure-time audit (cmake/SparkThirdPartyAudit.cmake) — see the ThirdParty Dependencies Audit page. ThirdParty/ is in .promptignore, so Claude does not see its contents (correct — it is not project code).


Architectural Defects Found by the 2026-07-18 Sweep (now fixed)

A multi-agent defect sweep (2026-07-18) confirmed and fixed 41 local defects across 31 files, plus three architectural ones that were fixed in follow-up commits the same day:

  1. Per-client reliable-message state was server-wide (fixed). Receive-side dedup/ACK/ordered state lived per-NetworkManager, not per sender, so with ≥2 clients, client B's sequence N was dropped as a duplicate of client A's and the merged broadcast ACK silently cancelled B's retransmit. Now all reliability state (dedup windows, reorder buffers, outgoing sequences, unacked/retransmit maps) lives in a per-peer PeerState keyed by ClientID, with per-peer unicast ACKs. Regression tests: Tests/TestReliableChannel.cpp (overlapping sequence spaces, ordered independence, ACK isolation).
  2. Core ECS phase systems were never registered in production (fixed). CreatePhaseSystemManager had no non-test callers. The gameplay lifecycle (GameplayLifecycleShared.cpp) now creates the manager during init and pumps UpdateAll each frame in the documented phase order; the dead StageBasedExecutor was deleted. Regression test: Tests/harden/Test_lifecycle_ecs_phase_wiring.cpp.
  3. Delta snapshot ACKs were never wired (fixed). Deltas now have their own ack echo — MessageType::DeltaAck carries the delta sequence (a different sequence space than transport ACKs, which would falsely advance baselines); clients echo it for applied Unreliable entity-state updates and the server routes the trusted sender to DeltaSnapshotManager::AcknowledgeSequence (cumulative, stale acks ignored). pendingDeltas is bounded at 256/connection. Regression tests: Tests/TestNetworkReplicationIntegration.cpp.

Bloat-split campaign (same day)

Six split waves reduced the over-threshold file count from 104 to 1 (103 files split into coherent sibling TUs plus small internal headers, all moves byte-identical and build+test verified). The final wave split the 18 Windows-only files (*Windows.cpp, ProcessWin32.cpp) using the MinGW cross-build (linux-mingw-release preset) for compile verification — each split TU was checked with x86_64-w64-mingw32-g++ -fsyntax-only against the real build flags, and the full cross-build stays green. The only remaining violation is GameModules/SparkGameMMOFPS/Source/Core/TFTypes.h (301 lines, a FROZEN CONTRACT header deliberately left alone). Gotchas discovered: (1) Tests/CMakeLists.txt links some module .cpp files by explicit path (e.g. TFOutfitStore.cpp), so splitting one of those requires adding the new sibling TU there too; (2) exe-only entry files (SparkEngineWindows.cpp) are listed in SPARK_ENGINE_ENTRY_POINTS, not globbed into the lib — their split parts must be added to that list; (3) in a network-restricted container, stage DirectXMath headers into build/.dxmath-cache/extract/DirectXMath-oct2024/Inc/ (plus a placeholder oct2024.zip) — the NuGet directxmath package is a working source when GitHub is blocked.


Source & Freshness

  • Original observation: .claude/knowledge/codebase-observations.md, last updated 2026-03-19.
  • Re-measured against codebase 2026-06-08.
  • Changes since the original:
    • ENABLE_NETWORKING default OLD OFF → NEW ON (networking now compiles in standard CI). Updated the most-common-trap framing accordingly.
    • Removed-globals list, EngineContext/EngineRuntime split, .promptignore scope, two tools/ dirs, and the head -50 partial-check note all re-verified as still accurate.
    • ThirdParty note expanded: now backed by ThirdParty/dependencies.lock + cmake/SparkThirdPartyAudit.cmake (both confirmed present).
    • Noted docs/api/ may be empty on a fresh checkout (0 generated pages present in the working tree at re-measure time).

Related Pages

SparkEngine Wiki

Website Entry Points

Getting Started

Engine Subsystems

Gameplay & Tools

Platform Support

Graphics

Advanced

Development & Process

Research & Analysis

Engineering Notes & Audits

Specifications

Reference

Clone this wiki locally