Files
2nd/00_Raw/2026-04-24-Skybound_Code_Structure_Audit_and_Stabilization_Plan.md
T
2026-04-24 23:24:07 +09:00

7.1 KiB

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.