Repository navigation
Workflow Patterns
Audience: Programmers | Mixed
Thread Context: N/A (development/process reference)
Platform/Backend Scope: All platforms (commands shown for Linux/bash; see notes for Windows/macOS equivalents)
Recurring workflows that have proven consistently effective for SparkEngine development tasks. These patterns reduce time spent on overhead and increase the reliability of outputs. Apply them proactively rather than waiting until something goes wrong.
They apply to all SparkEngine development sessions. Most correspond to gaps where the "obvious" approach is slower or less reliable than the pattern described here.
When a task touches multiple areas of the codebase (e.g., adding a feature that spans ECS + Graphics + Editor), launch 2-3 Explore agents in parallel rather than sequentially. Each agent gets a specific search focus.
Agent 1: "Search for existing implementations of X in SparkEngine/Source/Engine/ECS/"
Agent 2: "Find all usages of Y in SparkEditor/Source/"
Agent 3: "Identify patterns for Z in Tests/"
Do not use a single Explore agent for a broad multi-area search — it will either miss areas or spend too long on one area.
When to apply:
- Task involves code in 2+ directories
- Scope is uncertain and you need to map the codebase before planning
- Looking for an existing implementation before writing new code (always check first)
Notes:
- Use read-only exploration agents for codebase research; reserve planning agents for architectural design work after exploration.
- 3 agents maximum per parallel batch; quality over quantity.
When several agents (or scripts) share one build tree, route every configure and build through the lock wrapper instead of an ad-hoc flock:
tools/build-lock.sh -- cmake --build build/linux-gcc-release --target SparkTests -j4
tools/build-lock.sh --timeout 600 -- cmake --preset linux-gcc-release -DBUILD_TESTS=ON
tools/build-lock.sh --status # who holds it, and is the holder still alive?The lock defaults to build/.spark-build.lock (--lock / SPARK_BUILD_LOCK override it). What the wrapper guarantees, and why each matters:
-
Bounded wait. It gives up after
--timeoutseconds (default 1800,SPARK_BUILD_LOCK_TIMEOUT) with exit 75 and a holder report, instead of queueing forever. -
Holder record. The holder writes pid, host, start time, cwd, and command into the lock file; waiters print it every
--report-everyseconds (default 60). -
No inherited descriptors. The command runs with the lock fd closed (and in its own process group via
setsidwhen available), so a daemon or detached helper it spawns cannot keep the lock after the wrapper exits. - Orphan detection. If the recorded holder is dead but processes still have the lock file open, it says so and lists them. It never kills anything.
Do not guard a shared lock with a detached waiter that matches processes by command line (pgrep -f / pkill -f): the waiters' own command lines match the pattern. On 2026-09-24 such a helper inherited the build lock, waited for "no cmake --build build/... process", and deadlocked every queued build — including itself, since pkill -f on the same pattern also killed the invoking shell. Wait on a PID (kill -0 $PID) and hold locks only in the process that took them. Regression coverage: Tests/Tools/test_build_lock.py (CI job validate-ci-tools).
linux-gcc-release keeps ENABLE_LTO=ON because shipping builds use it. With LTO every Release object carries -flto=auto: compiling is cheap and the optimizer runs again at link time over all of SparkTests (≈800 objects plus the whole-archive engine library). Every engine change repeats that link. For an iterate-and-test tree, turn it off and add a compiler cache and a faster linker:
sudo apt-get install -y ccache mold
ccache -o max_size=8G -o base_dir="$PWD" -o hash_dir=false \
-o sloppiness=pch_defines,time_macros,include_file_mtime,include_file_ctime
cmake --preset linux-gcc-release -DBUILD_TESTS=ON -DENABLE_LTO=OFF \
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_LINKER_TYPE=MOLD- LTO off moves optimization back into each compile. Test coverage of the LTO configuration stays with the CI Release lanes.
-
ccache turns a snapshot switch or a reconfigure into cache hits. The
pch_defines,time_macrossloppiness is required, or ccache refuses to cache files that use a precompiled header. CI sets the same values inCCACHE_SLOPPINESS(.github/workflows/build.yml). -
mold (
CMAKE_LINKER_TYPE, CMake 3.29+) links the large test binary in seconds instead of GNUld's tens of seconds. -
SparkTestsprecompiled header.Tests/CMakeLists.txtprecompilesTestFramework.hplus the standard headers the test files share. It holds no engine or platform header, so test-local#defines before engine includes keep their meaning. Sources compiled intoSparkTestsfrom outsideTests/skip it. Lanes configured with-DCMAKE_DISABLE_PRECOMPILE_HEADERS=ON(the Windows sccache lanes and clang-tidy) build without it and still catch missing#includes.
Changing ENABLE_LTO, the launcher or the linker type changes every compile command, so the first build after the reconfigure is a full rebuild. Run it through tools/build-lock.sh when the tree is shared.
Cloud sessions get a fixed writable-disk allowance, and parallel agents fill it fast. Measured on 2026-09-25 (Linux GCC):
| Item | Size |
|---|---|
build/linux-gcc-release (-DENABLE_LTO=OFF, tests and game modules) |
≈2 GB |
Sanitizer Debug tree (ASan/UBSan, -g1) |
≈11 GiB (full -g is larger) |
| ccache store | its max_size (3–8 GB is plenty) |
Detached git worktree of the repo (no build) |
the checkout size, before any build |
-
Build privately only when needed. A private build per agent multiplies the first row. Prefer the shared tree under
tools/build-lock.sh, restricted to the targets you need (--target SparkTests). -
Scratch locations. Put scratch worktrees and private builds in the session scratch directory, not in the repo, and
git worktree removethem when done. -
Out-of-space errors. When "No space left on device" appears, delete regenerable outputs first: stale private builds, abandoned worktrees, then
ccache -C. Delete a scratch folder only after proving its contents are committed.
tools/architecture-viz/generate_code_city.py renders the tracked source tree as an interactive three.js city: projects are blocks, subsystem directories are districts, and each file is a building whose footprint and height grow with its line count. Selecting a building draws its resolved includes (blue) and includers (orange) and lists the readiness work items whose entry points name it; the color modes cover project, file kind, size, 180-day churn, readiness status and include fan-in.
python3 tools/architecture-viz/generate_code_city.py # writes build/code-city/index.html
python3 Tests/Tools/test_code_city.py # layout, include resolution, embedded dataThe page loads three.js from jsDelivr and embeds all data, so it opens straight from disk. The Code City Pages workflow (.github/workflows/code-city-pages.yml) regenerates it on pushes to Working and deploys it to GitHub Pages, which the README links to; Pages must be enabled with Settings → Pages → Source: GitHub Actions. Useful for orientation before a cross-subsystem change: search a file or class, then follow its include arcs.
After any change that adds, renames, or removes public headers, ECS components, systems, editor panels, or tests — run the doc scripts before committing. The fastest reliable option is the master script:
docs/update-all-docs.sh # runs all six doc scripts in order (~30s)
docs/update-all-docs.sh quick # skips API docs + flowchart (~10s)Or run only the two most commonly needed:
docs/generate-api-docs.sh check # regenerates API pages if headers changed
docs/sync-wiki.sh sync # updates wiki AUTO: sections (component/system/test counts)Then git add any changed files under docs/api/ or wiki/ alongside the code change.
When to apply:
- Added or removed a
.hfile - Added or removed an ECS component or system
- Added or removed an editor panel
- Added or removed a test
Notes:
- The
check-formatCI job will not catch stale docs — it only checks C++ formatting. Stale docs surface as unexpected diffs in later sessions. -
docs/generate-api-docs.sh checkuses checksums — it is fast when nothing changed. -
docs/sync-wiki.sh syncupdates<!-- AUTO:* -->markers in wiki files. Never edit those markers manually.
Run pre-push checks in this specific order — each step catches a different error class, and the order minimizes wasted time:
# 1. Format first (fastest, most common failure)
find SparkEngine/Source GameModules SparkEditor/Source SparkConsole/src SparkShaderCompiler/src \
-not -path '*/Metal/*' \( -name '*.h' -o -name '*.hpp' -o -name '*.cpp' \) | \
xargs clang-format --dry-run --Werror 2>&1
# 2. CMake configure (catches missing headers, broken includes)
cmake --preset linux-gcc-release 2>&1 | tail -20
# 3. Build (catches compile errors)
cmake --build build/linux-gcc-release --parallel $(nproc) 2>&1 | tail -30
# 4. Tests (catches regressions)
ctest --test-dir build/linux-gcc-release --output-on-failure --no-tests=error
# 5. Docs (catches stale auto-generated content)
docs/update-all-docs.shStop at the first failure; fix it before proceeding to the next step.
Notes:
- Step 1 (format) is cheap (~5 seconds). Do it even for tiny changes — MSVC code often reformats differently.
- Step 2 (configure) detects include-path issues before a full compile.
- Step 5 (docs) is only needed when step 1 touches headers or structural files.
- See Build Optimizations for the
--parallel $(nproc)flag in step 3.
Always prefer a named preset over manually constructing -B build -DCMAKE_BUILD_TYPE=... flags for local development.
# Preferred
cmake --preset linux-gcc-release
# Only use manual flags when a custom toggle is needed
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DENABLE_NETWORKING=ON \
-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++Notes:
- Presets are defined in
CMakePresets.jsonand are authoritative. - Available presets include:
windows-debug,windows-release,windows-shipping,windows-development,linux-gcc-debug,linux-gcc-release,linux-clang-debug,linux-clang-release,linux-shipping,linux-development,linux-mingw-release,linux-mingw-debug,macos-debug,macos-release,macos-metal,macos-moltenvk,macos-shipping(Apple Silicon, experimental),linux-fuzz,minimal, plus the CI-specificci-linux-asanandci-linux-tsan. - Note: the sanitizer/GCC/Clang CI jobs use manual
cmake -B buildflag invocations rather than the matching preset, so to reproduce those jobs exactly use the commands in CI Reproducible Builds, not the preset. - If a preset produces unexpected behavior, check
CMakePresets.jsonfor the exact flags it sets rather than guessing. - Delete
build/if switching between preset and manual configure — the cache will conflict.
Before writing any new code in a file, check its size. SparkEngine's anti-bloat thresholds are ~500 lines for .cpp and ~300 for .h (guidelines for when to pause, not hard limits — see CLAUDE.md). If a file is over the threshold and doing multiple jobs, trim it first:
# Check size of a file before editing
wc -l SparkEngine/Source/Utils/SparkConsole.cpp
# If over threshold:
# 1. Find dead methods (no callers outside the class)
grep -n "void SimpleConsole::" SparkEngine/Source/Utils/SparkConsole.cpp | head -30
# 2. Delete them
# 3. Then make your actual changeFor any new public method being added:
- Search for existing methods that do something similar.
- If one exists, extend it — don't add a new one.
- If there are now 2 similar methods after your change, remove the older one.
For any new system/class being added:
- Search for an existing class that could be extended.
- If adding, remove something of equivalent complexity.
When to apply:
- Every time you open a file to edit it
- Before every PR — check net line delta: should be <= 0 for refactors, minimal positive for features
- When a file starts feeling hard to navigate
Notes:
- Removal is not "going backwards" — it is the primary maintenance activity.
- Delete dead code; don't comment it out. Git history exists.
-
tools/check-bloat.shenforces the size thresholds and runs as part oftools/validate-all.sh.
Every session should start in this exact order before doing anything else:
-
git fetch origin Working && git log --oneline HEAD..origin/Working | wc -l— assess how far behind. -
git rebase origin/Working— sync with upstream. - Resolve any rebase conflicts (see Git Rebase Conflicts).
-
cat wiki/_Sidebar.md— load persistent context from the wiki. - Read the wiki pages relevant to the current task.
-
Bloat check — run before touching anything:
find SparkEngine/Source SparkEditor/Source SparkConsole/src GameModules \ -name '*.cpp' | xargs wc -l | sort -rn | head -15
- Then start reading code or planning.
Skipping steps 4-5 means starting each session without accumulated knowledge. Skipping step 6 means walking into a bloated file blind.
Notes:
- If
wc -loutput is 0 in step 1, the branch is up to date — skip the rebase. - Never start reading or editing code before completing the git sync. Stale code leads to conflicts on push.
- The bloat check takes ~2 seconds.
- Original entry:
Effective SparkEngine Development Workflows, last updated 2026-03-14. - Verified against codebase 2026-06-08.
- 2026-09-24: added the Code City section (
tools/architecture-viz). - 2026-09-24: added the shared-build-directory lock section (
tools/build-lock.sh) after a parallel-agent build deadlock. - 2026-09-25: added the disk-budget section after repeated out-of-space stalls during parallel readiness work.
- Updated / found stale:
- Doc-sync section now leads with
docs/update-all-docs.sh(the master script), which is the current recommended one-shot; the two-script combo is kept as a faster subset. - Pre-push step 5 changed to
docs/update-all-docs.shto match currentCLAUDE.mdpre-commit guidance (was two separate scripts). - Anti-bloat thresholds updated to the current ~500
.cpp/ ~300.hguideline values (source quoted older 400/200 figures); notedtools/check-bloat.shas the enforcer. - CMake preset list refreshed against
CMakePresets.json; added newci-linux-asan/ci-linux-tsanpresets and the caveat that sanitizer/GCC/Clang CI jobs use manual flags, not presets. - Bloat-check
findpath generalized toGameModules(matches currentCLAUDE.md; source pinnedGameModules/SparkGame/Source). - Cross-references retargeted to the migrated wiki pages. Removed references to
clang-format.md,ai-bloat-pattern.md,codebase-observations.mdsource files (not migrated to this folder).
- Doc-sync section now leads with
Published from 614bca17b41e. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Platform Certification
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Fuzz Policy and Parser Security
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Crash Reporting
- Golden Image Testing
- Utilities
- Testing
- Fuzz Policy and Parser Security
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- CI Controlled-Failure Rehearsal
- Release Publication Stages
- Session Handoff 2026-09-27
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Online Service Boundary
- Server Operations Runbook
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers