# Skybound Code Structure Audit and Stabilization Plan **Date**: 2026-04-24 **Project**: Skybound Protocol **Author**: Codex **Status**: Raw analysis logged before implementation ## 1. Overview This document records the first code-level audit after reviewing Skybound-related wiki documents. The goal was to compare the documented architecture with the actual code path in `/Volumes/Data/project/Antigravity/Skybound`, identify design and feature risks, and define the first stabilization pass. ## 2. Documents Reviewed - `Topics/Skybound/Skybound-Knowledge-Hub.md` - `Topics/Skybound/01_Core_Engine/Skybound-Modular-Game-Architecture.md` - `Topics/Skybound/01_Core_Engine/Game-Engine-Loop-and-System-Orchestration.md` - `Topics/Skybound/05_Project_Issues/2026-04-22_Engine_Stability_Audit.md` - `Topics/Skybound/02_Combat_AI/Combat-System-and-Bullet-Interaction-Pipeline.md` - `Topics/Skybound/04_Mechanics_Progression/Campaign_and_Dual_Loop_System.md` - `Topics/Skybound/04_Mechanics_Progression/InGame_Progression_System.md` - `Technical_Reports/2026-04-22_Boss_Battle_System_Implementation.md` ## 3. Code Structure Observations Skybound's actual runtime center is `src/features/game/hooks/useGameEngine.ts`. It directly instantiates and updates `EntityManager`, `StageDirectorSystem`, `SpawnerSystem`, `CombatSystem`, `ProgressionSystem`, `ModularWeaponSystem`, `TacticalSystem`, `HazardSystem`, `BossSystem`, `EffectSystem`, and `GameRenderer`. `SystemManager.ts` exists and documents a centralized orchestration pattern, but the active engine path does not use it. This is not inherently broken, but it creates a mismatch between the documented orchestrator and the actual execution path. React is primarily responsible for scene composition and modal/UI handling through `GameSceneRenderer.tsx`, while Zustand state is centralized in `useGameStore.ts`. ## 4. Confirmed Problems ### 4.1 Build Gate Is Broken `npm run build` fails. Representative causes: - `App.tsx` imports `useGameEngine` but does not use it. - Legacy `src/features/game/combatSystem.ts` is still included in TypeScript compilation and has type drift against current domain models. - `useSceneAudio.ts` does not include `HANGAR` in `SCENE_AUDIO`, despite `Scene` including `HANGAR`. - `SystemBoss.phase` is typed as `1 | 2 | 3`, while `bossRegistry.ts` contains a 4-phase boss. - `GameRenderer.ts` calls `CanvasRenderingContext2D.close()`, which should be `closePath()`. - `EntityManager` initializes enemies with `movePattern: 'NONE'`, which is not part of the current `Enemy.movePattern` union. ### 4.2 Timeline Spawn Intents Are Not Wired `StageDirectorSystem` dispatches `SCRIPTED_SPAWN` and `SPAWN_MODE` intents, but `useGameEngine.ts` does not route those intents to `SpawnerSystem`. `SpawnerSystem` already provides `notifyScriptedSpawn()` and `activateSwarmBurst()`, so the design exists but the active dispatch wiring is missing. Expected impact: scripted waves and burst events may not appear as designed, leaving procedural spawning to carry too much of the combat pacing. ### 4.3 Campaign Stage State Is Not a Single Source of Truth `StageDirectorSystem` receives `campaignStageIndex` when created, but `GameState.currentStage` still defaults to `1` and is not initialized from the selected campaign stage. Expected impact: Standard campaign can show stage progression in UI while the engine continues to evaluate boss selection, damage curves, backgrounds, rewards, and tech-part rolls as Stage 1. ### 4.4 StageManager Mode Can Drift from Store Mode `StageManager` has its own internal `mode`, defaulting to `BLITZ`. The UI updates Zustand `stageMode`, but `stageManager.setMode()` is not called from the store action. Expected impact: Standard campaign clear can call `stageManager.onStageClear()` while the singleton still believes it is in Blitz mode, preventing next-stage campaign progression. ### 4.5 COMMS Events Are Not Rendered `COMMS` events are emitted by engine systems, and `CommsOverlay.tsx` exists, but `useGameEngine.ts` does not map `COMMS` events to `useGameStore.setComms()`. `CommsOverlay` is also not rendered by `App.tsx`. Expected impact: mission dialogue, warning text, and tactical pacing messages are silently dropped. ### 4.6 Starter Weapon Timer Is Not Cleaned Up `useGameEngine.ts` starts a `setTimeout()` for starter weapon selection, but cleanup does not clear the timer. Expected impact: if the engine unmounts quickly, a stale timer can emit a level-up modal or pause signal from an old engine instance. ### 4.7 Skill Sync Boundary Is Fragile `GameSceneRenderer.tsx` calls store `addSkill()` and then engine `applySkill()`. `ProgressionSystem.applySkillSelection()` says Zustand owns skill increments, but also mutates `state.skills` as a fallback. Expected impact: this usually works because of the store subscription bridge, but the ownership boundary is not clean and may cause missed or inconsistent skill levels under timing stress. ## 5. Stabilization Plan Priority order: 1. Restore TypeScript build by removing obvious compile blockers and isolating legacy drift. 2. Wire `SCRIPTED_SPAWN` and `SPAWN_MODE` intents from `StageDirectorSystem` into `SpawnerSystem`. 3. Initialize engine `currentStage` from `stageMode` and `campaignStageIndex`. 4. Keep `StageManager` mode synchronized with Zustand `stageMode`. 5. Route `COMMS` events into the store and render `CommsOverlay`. 6. Clear the starter weapon timer during engine cleanup. ## 6. Verification Targets - `npm run build` - `npm run lint` as a visibility check, with the expectation that broad historical lint debt may remain after the first stabilization pass. ## 7. Implementation Result The first stabilization pass was applied immediately after this raw audit. ### 7.1 Build Recovery - Removed an unused `useGameEngine` import from `App.tsx`. - Added `HANGAR` to `SCENE_AUDIO` in `useSceneAudio.ts`. - Excluded legacy `src/features/game/combatSystem.ts` from `tsconfig.app.json` because the active runtime path uses `src/features/game/systems/CombatSystem.ts`. - Expanded boss phase typing from `1 | 2 | 3` to `number` so 4-phase registry bosses are valid. - Fixed `EntityManager` default enemy `movePattern` from invalid `NONE` to `SIDE_TO_SIDE`. - Fixed `CanvasRenderingContext2D.close()` to `closePath()`. - Removed compile-blocking unused locals from active systems. ### 7.2 Runtime Wiring Recovery - `SCRIPTED_SPAWN` intents now call `SpawnerSystem.notifyScriptedSpawn()`. - `SPAWN_MODE` intents now activate `SWARM_BURST` or enqueue `MINI_BOSS` spawns. - Engine `currentStage` now initializes from `campaignStageIndex + 1` in Standard mode. - Zustand `setStageMode()` now synchronizes the `StageManager` singleton mode. - `COMMS` engine events now populate `useGameStore.activeComms`. - `CommsOverlay` is rendered while playing. - Starter weapon selection timer is cleared during engine cleanup. ### 7.3 Verification Result - `npm run build`: passed. - `npm run lint`: still fails due broad pre-existing lint debt, mostly `@typescript-eslint/no-explicit-any`, older helper files, and React hook lint findings. The first pass reduced the functional blockers but did not attempt a large-scale type cleanup.