# 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.
