Code cleanup audit, post-1.0 (2026-09-28)
Code cleanup audit, post-1.0 (2026-09-28)
Section titled “Code cleanup audit, post-1.0 (2026-09-28)”Read-only audit of main at 4a418a6 (BUILD 206 = store build 37). Nothing was edited. The test
suite was run once, on a copy of the repo in a scratch directory so the real checkout wasn’t
touched: 320 scripts, 3151 tests, 3147 passing, 4 risky, 0 failing, 80.8 s.
Method: symbol-reference scans over the tracked .gd/.tscn/.json/.sh files, leaving out
addons/, platform/ (symlinks) and _archive/; per-column read/write scans of EnemyPool; an
argument-count check on every enemies.add( call; per-test timings from GUT’s JUnit output; git
branch, worktree and size checks; and matching function names cited in the docs against the code.
Confidence tags: [Certain] checked directly in the code or by running it. [Likely] strong
evidence with one unchecked assumption. [Guessing] an inference.
Det. risk means removing or changing it could move the seed-1234 baselines in
tests/test_determinism_survival.gd, tests/test_determinism.gd or tests/test_determinism_coop.gd.
state_checksum() hashes pilot pos/hp/xp/level; enemy pos/HP/aura_element/stacks/aura_remaining;
projectile, enemy-projectile and gem positions; and drones. Anything outside that set, and anything
that draws no RNG, is safe to delete.
Executive summary: top 10 by value/effort
Section titled “Executive summary: top 10 by value/effort”| # | Finding | Why it matters | Effort | Det. risk |
|---|---|---|---|---|
| 1 | Halo Chain kills pay 0 XP and add 0 wave biomass. HaloChainDirector.spawn_pack calls enemies.add() with 11 args, so biomass_v falls back to 0. The bible says biomass: 2. This is the same bug class as the 2026-08 story-mode zero-XP bug, live in Survival (spawn_table.gd:29,77). Splitter children (sim.gd:2682) have the same problem, and may be intended to. |
A real gameplay bug, not cleanup. It also means the wave-pressure maths never counts Halo packs. | S | yes (spawn pacing; Halo unlocks late, so it is probably outside the 600-tick window, but re-run the baselines) |
| 2 | Remote config advertises boss stats that nothing reads. build-206.json manifests enemies.funzo.hp/armor/speed/contact_damage (and warden/graviton) as patchable. FunZo.spawn uses FUNZO_HP etc., and _enemy_types[TYPE_FUNZO] = {}. Only the Eye reads the bible. FunZo is the only survival boss (V01_FUNZO_ONLY). |
A balance patch to FunZo HP would publish “successfully” and do nothing. | S (add to EXCLUDED_FIELDS), or M (make the boss directors read the bible) |
no, if excluded. Reading the bible is also no while the values match (they do today). |
| 3 | Delete the 5 stale agent worktrees and review 3 unmerged commits. .claude/worktrees/* is about 385 MB. worktree-agent-a0a9… (+ 2 uncommitted files) and -a46b… hold 3 commits that are not on main (2f7dc25 stats-sheet redesign, 4895303 death-screen damage breakdown, 4c57fba WIP TestFlight features). networked-multiplayer-mb-mc is 54 commits ahead and dates from July. |
Housekeeping, but check the 3 commits before deleting anything. | S | no |
| 4 | Dead Sim pacing and state. _next_boss_time/BOSS_FIRST_TIME/BOSS_INTERVAL are write-only. _spawn_swarm_burst + SWARM_BURST_* + _swarm_burst_fired have no live caller. _boss_spawn_count is write-only (5 increments, 0 reads). _spawn_accum and other_area() are unused. 4 boss tests assert on _next_boss_time. This is already on NEXT_TASKS. |
Misleading knobs, and tests of behaviour that no longer exists. | S | no (none of it is in the checksum, and none of it draws RNG) |
| 5 | Reactions-mode residue in EnemyPool and the render path. primed is only ever written 0, but main.gd:2222-2234 reads it per enemy per frame. react_cd is decremented every tick (elemental_system.gd:389) and never set or read. xp_val is written and never read. Elemental.apply() + ContentDB.reaction() are reachable from tests only. On NEXT_TASKS. |
Per-frame and per-tick waste. The 13-positional-arg add() is the trap behind finding 1. |
M | no (aura_element/stacks/aura_remaining ARE in the checksum; leave those alone) |
| 6 | Retire the A/B look switches that have been decided. VisualLook.glass_ui (true) keeps the 631-line LegacyLevelUpPanel + its 168-line test + non-glass branches in pause_menu/level_up_base. VisualLook.nova, plasma_nova and ThreatRadar.radar_v2 each keep an old path alive (about 40 branch sites in about 15 files). |
Roughly 1,000+ lines removable once Chris confirms each pick. | M | no (render/UI only) |
| 7 | Consolidate the main.gd dev-only const flags. PERF_BENCH, AUTO_SHOWCASE, MEASURE_FULL_QUALITY, SHIP3D_MEASURE and RECORD_REPLAY are all false. Four of the five are “never ship true” benches. The MEASURE_FULL_QUALITY comment says “adaptive quality runs”, but ADAPTIVE_QUALITY := false. |
About 150 lines of main.gd, plus one misleading comment. Move them to a DevBench helper or a single BuildConfig dev flag. |
S–M | no |
| 8 | Split sim.gd: 3,053 lines, not “~2,400”. Since the July split it has gained +1,881/−650 lines. Carve out SpawnSystem, StorySpawner, DimensionSystem, SimDevSeams and per-boss on_death() (see §3). |
The biggest structural win. It also fixes the CLAUDE.md drift. | L | yes, only if RNG draw order changes. Pure relocation is safe; gate each step on the 3 determinism tests. |
| 9 | main.gd._new_run() is 545 lines, _process 348, _physics_process 215. Extract EnemyColorizer (pure, about 200 lines), MainDevCommands (about 200), and a render-rig builder. Already on NEXT_TASKS for _new_run. |
Makes main.gd testable in slices. That unblocks the “no e2e test of _new_run order” item on NEXT_TASKS. |
M–L | no |
| 10 | Beacon’s flank-jitter buff is documented but never wired. BeaconDirector.BEACON_FLANK_MULT is defined and referenced nowhere. The class header says Sim._move_enemies reads it, but EnemyBehaviors.step_walk only uses flank[i]. Only the telegraph buff is live. |
A feature gap, or a doc lie: decide which, then wire it or delete it. | S | wiring it: yes (Beacon is the last unlock, so probably outside the window). Deleting it: no. |
Also cheap and worth doing in the same pass: menu_theme.mp3 and survival_loop.mp3 are
byte-identical, 3.7 MB each, both shipped (§7). Upgrades.roll_choices() is a test-only RNG
trap (§1). scripts/verify-sim-refactor.sh has a stale baseline (180 scripts vs 320) (§6).
1. Dead code
Section titled “1. Dead code”| Finding | Evidence | Conf. | Effort | Det. risk | Suggested action |
|---|---|---|---|---|---|
EnemyPool.xp_val written, never read |
enemy_pool.gd:102,261,320,376. The only non-pool mention is a comment at sim.gd:1127. bible_overlay.gd:35 already labels it dead |
Certain | S | no | Remove the column + add()’s xp_v param. Doing so means updating all 22 add() call sites, so do it together with an add() redesign that takes a spec dict or named-arg builder, to kill the positional trap |
EnemyPool.primed is never set to 1, but is read every frame |
enemy_pool.gd:95,256,315,371; main.gd:2222-2234 lerps colour when primed[i]==1 |
Certain | S | no | Remove the column and the main.gd branch (NEXT_TASKS 2026-08-12) |
EnemyPool.react_cd decremented each tick, never set or read |
elemental_system.gd:389 is the only use |
Certain | S | no | Remove (NEXT_TASKS) |
EnemyPool.shape_id: only TYPE_CUSTOM (dev tool) sets it |
sim.gd:1833 (dev_spawn_custom), read in archetype_renderer.gd:787 |
Certain | — | no | Keep while the custom-enemy dev tool exists; note it as dev-only |
Sim._next_boss_time, BOSS_FIRST_TIME, BOSS_INTERVAL write-only |
sim.gd:14-15,614, 5 writes at 2645-2676, no reads. Tests test_boss.gd:225, test_eye.gd:414, test_funzo.gd:383, test_graviton.gd:392 assert on it |
Certain | S | no | Delete, plus the 4 tests. BOSS_FIRST_TIME is also used as a convenient “late enough” time in 4 tests; replace it there with a literal |
Sim._spawn_swarm_burst, SWARM_BURST_TIME, SWARM_BURST_COUNT, _swarm_burst_fired |
sim.gd:82-83,616,1777. Only caller is tests/test_areas.gd |
Certain | S | no | Delete (NEXT_TASKS) |
Sim._boss_spawn_count write-only |
Incremented in all 5 boss spawn()s. Its declaration comment at sim.gd:615 (“5-way rotation counter”) describes the removed rotation. No reads anywhere, tests included |
Certain | S | no | Delete |
Sim._spawn_accum, Sim.other_area() |
sim.gd:519, sim.gd:1413: zero references |
Certain | S | no | Delete |
Upgrades.roll_choices(rng, …): test-only, and a determinism trap |
sim/upgrades.gd:7; only tests/test_upgrades.gd. Takes a raw rng. CLAUDE.md says to roll via upgrade_system.roll_upgrade_choices |
Certain | S | no | Delete, so nobody calls it with sim.rng |
Elemental.apply() + ContentDB.reaction() (reactions archive) |
Only tests/test_elemental.gd, test_mods_in_sim.gd, test_content_db.gd |
Certain | S–M | no | Move to _archive/ or delete, along with its tests (NEXT_TASKS) |
Sim.RULESET_REACTIONS / ruleset field |
sim.gd:382-391. Only SURVIVAL is ever set; 4 ruleset ==/!= guards |
Certain | S | no | Collapse the guards and drop the field |
| Dead consts | BeaconDirector.BEACON_FLANK_MULT (beacon_director.gd:30, see summary #10); EliteKit.WEB_SLOW_MULT (elite_kit.gd:87, “PlayerState.BEAM_SLOW_MULT mirrors this”, so a duplicate source of truth); ThreatRadar.V2_TOP_K/V2_SIZE/V2_EDGE_INSET (threat_radar.gd:26-29); ElementPalette.PLAYER_CORE; FunzoTreatments.TREATMENT_COUNT; QualityManager.TARGET_FPS; UiTokens.BG_PANEL |
Certain (0 refs) | S | no | Delete. For WEB_SLOW_MULT, make PlayerState.BEAM_SLOW_MULT reference it, or delete one of the two |
| Dead functions (0 refs anywhere) | Ship3DRenderer.tilt_idle (ship3d_renderer.gd:195), BossEdgeIndicator.arrow_direction (:100), ButtonPrompt.style_for_device (:44), MetaShopPanel._rebuild_current (:905, comment says “for tools”, but no tool calls it), UpgradeCopy.leech_cap_per_tick (:21) |
Certain | S | no | Delete |
| Test-only production functions (69 found) | Most are legitimate test accessors (hp_fill_width, active_count, …). Notable non-accessors: SpawnDirector.spawn_count_for, DroneDirector.drone_kinds_for, StoryState.tutorial_locked, MetaState.has_seen, UpgradeSystem.upgrade_choice_display, UpgradeSystem.live_dps (the last is read by test_results_panel only; check whether the results panel was meant to show it) |
Likely | S | no | Review the non-accessor ones. Accessors stay |
Ship3DRenderer (244 lines) + ShipClasses |
Only live-code user is main.gd, behind SHIP3D_MEASURE := false (“DISABLED 2026-07-01, live SubViewport-3D broken on tvOS”). Also used by tools/ship_preview |
Certain | S | no | Keep for the tool; drop the main.gd wiring (see §2) |
Power-ups (gated off by POWERUPS_ENABLED := false) |
sim.gd:97-114 consts, _collect_powerups/_apply_powerup (sim.gd:2858-2893), render/powerup_renderer.gd (52 lines), 5 main.gd refs |
Certain | S | yes: the skirmisher death must keep rng.randi_range(0, POWERUP_KINDS-1) (sim.gd:2691) or the spawn stream shifts |
Park until Chris decides. If deleted, keep a bare rng.randi_range(0, 3) with a comment, or re-pin via bh-determinism-repin |
Boss2 (gated by BOSS2_ENABLED := false) |
sim/boss2.gd (245), boss2_state, renderer, 2 tests (232 lines), elite path sim.gd:2030 |
Certain | M | no (the elite path is behind the const and draws nothing) | Chris’s call: archive it like reactions, or keep it for revival |
| Warden elite path only reachable in Aurora/Ember Reach | sim.gd:2023 (current_area != HOME). NEXT_TASKS says real play never enters them |
Likely | S | no | Fold into the NEXT_TASKS decision |
_archive/ (1,168 lines) duplicates git tags |
Tags pre-reactions-archive-2026-07-09 and pre-portal-popup-archive-2026-07-10 exist. _archive/ has no .gdignore, so Godot still parses its .gd files. The memory note records a class_name collision from this |
Certain | S | no | Delete _archive/ (the tags keep the history), or at least add _archive/.gdignore |
Teaser wormhole (V01_FUNZO_ONLY gate) |
_spawn_teaser_wormhole (sim.gd:2819) has no live caller (retired 2026-07-07, per the comment at sim.gd:2698-2702). But _update_teaser_wormhole() still runs every tick (sim.gd:922) on an always-empty dict, and main.gd still drives WormholeRenderer + _check_teaser_event for it |
Certain | S | no (it draws no RNG and isn’t hashed) | Delete the spawn/update/consume trio, WORMHOLE_RADIUS, V01_FUNZO_ONLY, WormholeRenderer, main._check_teaser_event, test_teaser_wormhole.gd, test_wormhole_renderer.gd, test_v01_funzo_only.gd |
Orphan .uid files (untracked, on disk) |
17, for example ui/crystals_levelup_panel.gd.uid, ui/control_hint.gd.uid, sim/decoy_state.gd.uid, tests/test_zzz_verify_TEMP.gd.uid |
Certain | S | no | find . -name '*.gd.uid' for files with no matching .gd → delete |
Other enemies.add() calls that drop biomass_v
Section titled “Other enemies.add() calls that drop biomass_v”| Call site | Args | Intended? | Conf. |
|---|---|---|---|
halo_chain_director.gd:80 (Halo Chain packs, live in Survival) |
11 | No. The bible has biomass: 2, and the code passes xp_value |
Likely (bug) |
sim.gd:2682 (splitter children) |
10 | Probably not: it passes sw["xp_value"], which suggests XP was intended. 0 XP since the XP→biomass switch |
Likely |
boss2.gd:219 (Boss2 summons) |
10 | Unclear. Boss2 is disabled | Guessing |
funzo.gd:252 (jesters), enemy_attacks.gd:99 (tank missiles) |
11/12 | Yes (commented “no XP farming”) | Certain |
5 boss spawn()s |
11 | Yes (_xp_worth falls back to BOSS_XP_BIOMASS) |
Certain |
Suggested action: a regression test per spawn path, on the real spawn→kill→_sweep_dead path. Then
make biomass_v required: move it before the defaulted params, or have add() take a spec.
2. Hardcoded feature and dev flags
Section titled “2. Hardcoded feature and dev flags”Every const X := true/false outside tests (23), plus the runtime static var switches.
| Flag | Value | Gates | Dead? | Conf. | Suggested action |
|---|---|---|---|---|---|
main.gd:158 MEASURE_FULL_QUALITY |
false | fps overlay + pin L0 (main.gd:1347-1360,1756) |
Dead path. Its comment (“adaptive quality runs”) contradicts ADAPTIVE_QUALITY := false |
Certain | Delete, with the measure_label/_fps_* vars |
main.gd:162 ADAPTIVE_QUALITY |
false | QualityManager.tick (main.gd:1754). Memory says “re-enable at release”, but 1.0 shipped with it off |
Off in shipping | Certain | Chris’s call: re-enable, or accept “always L0” and strip the auto-degrade half of QualityManager (Debug Settings still sets tiers manually) |
main.gd:167 PERF_BENCH (+_DWELL_S, _MAX_LEVEL, _RUN_S) |
false | tier-cycling bench (main.gd:475,749,1326,1744) |
Dev-only, “never ship true” | Certain | Move to a DevBench RefCounted helper, or behind BuildConfig.dev_tools() + a Debug Settings row instead of a const edit |
main.gd:176 AUTO_SHOWCASE (+SHOWCASE_*) |
false | hands-free attract/warp showcase (main.gd:444-472,1326,1734) |
Dev-only | Certain | Same as PERF_BENCH |
main.gd:187 V01_LOCK_AREAS |
false | sim.areas_enabled=false (main.gd:908) |
False branch dead | Certain | Delete (Dimensions are live) |
main.gd:196 V01_LOCK_COOP |
false | P2 join (main.gd:848) |
False branch dead | Certain | Delete, or keep as a documented kill switch |
main.gd:203 SHIP3D_MEASURE |
false | Ship3DRenderer spike (main.gd:9,1198) |
Dead (“DISABLED 2026-07-01”) | Certain | Delete the main.gd wiring |
main.gd:207 RECORD_REPLAY |
false | ReplayRecorder (main.gd:906,1607) |
Dev-only, but feeds tools/story_board |
Certain | Keep; make it runtime (dev console/Debug Settings) instead of a const |
main.gd:254 ENEMY_HIT_FLASH |
true | hit-flash tint (3 sites) | Decided (“GRAPHICS CANDIDATE 1”) | Likely | Inline, drop the flag |
render/archetype_renderer.gd:146 USE_BAKED_GLOW |
false | baked-glow path (:353) |
Dead path. Its outputs (ghost_glow_baked.png, orbiter_glow_baked.png) were never committed |
Certain | Delete the branch + tools/glow_bake/bake_{ghost,orbiter}_glow.gd |
render/player_renderer.gd:52 USE_LIT_HULL |
true | lit normal-mapped hull | Decided | Likely | Inline; drop the 3 pass_test branches in test_player_renderer.gd |
render/player_renderer.gd:60 USE_BAKED_HULL |
true | baked 3D hull sprite | Decided | Likely | Inline; drop 6 pass_test branches |
render/warp_effect.gd:25 BLUR_V2 |
true | warp afterimages (10 sites across 3 files) | Decided | Likely | Inline, delete the V1 streak code |
render/warp_effect.gd:32 SPEED_LINES |
false | streak lines | Dead path | Certain | Delete |
sim/boss_rotation.gd:10 V01_FUNZO_ONLY |
true | now only the teaser-wormhole spawn guard (sim.gd:2820), and that spawn function has no caller |
Dead | Certain | Delete with the teaser machinery (§1) |
sim/enemy_behaviors.gd:168 CHARGER_CRUISE_WHILE_CHARGING |
false | Charger movement during charge | Dead path | Certain | Delete the branch (det: no, elites are absent from the baseline) |
sim/enemy_behaviors.gd:250 ELITE_SPIDER_RAY_USE_MOVING_EMITTER |
true | legacy ray path “kept for an easy revert” (:280+) + test_elite_powers.gd:126 legacy tests |
Legacy path dead | Certain | Delete the legacy path + its tests |
sim/sim.gd:68 BOSS2_ENABLED |
false | Boss2 elite | see §1 | Certain | Chris’s call |
sim/sim.gd:101 POWERUPS_ENABLED |
false | power-up drops | see §1 (keep the RNG draw) | Certain | Chris’s call |
sim/sim.gd:224 AUTO_COLLECT_XP |
true | gem-drop XP path (sim.gd:2545) |
Gem-XP branch dead | Certain | Delete the gem branch only if physical gems are gone for good (det: no; with the flag true, kills spawn no gems) |
ui/start_menu.gd:17 V01_CRYSTALS_ONLY |
true | hides Story mode | Live gate, misnamed (there is no “Crystals” mode any more) | Certain | Rename to STORY_HIDDEN or similar; keep |
ui/touch_controls.gd:27 SYS_SLOT_IS_FEEDBACK |
true | touch sys-slot = feedback vs HUD toggle | Decided | Likely | Inline |
fx/damage_numbers.gd:17 MERGE |
true | number merging | Decided | Likely | Inline |
Runtime static var: VisualLook.nova/nova_ground/glass_ui/plasma_nova, ThreatRadar.radar_v2, Ship3DRenderer.topdown_override, TouchControls.slide_abilities |
mixed | A/B switches exposed in Debug Settings | Old paths are alive only for A/B | Certain | Once Chris confirms each pick, delete the losing path (see summary #6). LegacyLevelUpPanel is the biggest single deletion |
3. God files
Section titled “3. God files”sim/sim.gd: 3,053 lines (CLAUDE.md says “~2,400”), 94 funcs, 165 vars, 119 consts, 33% comments
Section titled “sim/sim.gd: 3,053 lines (CLAUDE.md says “~2,400”), 94 funcs, 165 vars, 119 consts, 33% comments”Growth since the 2026-07-02 split: +1,881 / −650 lines. Almost all of it went back into the conductor instead of into modules.
Responsibility (still in Sim) |
Functions (line) | ~Lines | Proposed module |
|---|---|---|---|
| Spawn pipeline | _spawn_one 1486 (136), _spawn_wyrm 1622 (82), _spawn_wave 1924, _spawn_enemies, _pick_extra_variety, _spawn_point, _spawn_due_elites, _spawn_boss_adds, _spawn_elite_swarmer_brood, _vary_stats, _element_for_spawn, _dim_buff, _update_spawn_phase, _set_spawn_phase, spawn_banner, total_biomass, _count_of_type, _build_enemy_types 710 (74) |
~650 | SpawnSystem (sim.spawn_system). RNG draw order is load-bearing: move bodies verbatim and gate on all 3 determinism tests |
| Story/tutorial spawns + rewards | enable_story, spawn_story_boss (46), spawn_story_enemy (78), _spawn_story_wyrm (50), grant_story_reward, _build_weapon_remap, drop_weapon_pickup, _collect_weapon_pickups, tutorial_locks |
~330 | StorySpawner. Also dedupe _spawn_story_wyrm against _spawn_wyrm (near-parallel, both set _wyrm_biomass) |
| Dimensions / areas | enter_area, _dimension_allowed_types, _remap_to_dimension, _update_dimension_hazard, _spawn_fire_hazard, _spawn_void_hazard, _award_dimension_crystals, _roll_dimension_choice, teaser wormhole trio, _spawn_phase_boss, _spawn_dimension_boss, _update_chalice_zones |
~330 | DimensionSystem (next to the existing dimension_hazards.gd) |
| Boss death rewards | _sweep_dead 2565 (144): 5 near-identical elif dead_type == TYPE_X branches (gold + crystals + reset + “BOSS DOWN”) |
~60 | A on_death(sim, pos) on each boss director; _sweep_dead dispatches through boss_rotation |
| Pilot survival | _update_dash, _apply_regen, stand_heal_rate, _hurt_player (49), _check_pilot_hit (39), _armor_reduction, armor_reduction_for, is_invulnerable, _all_pilots_down |
~220 | PilotSystem |
| Dev/playtest seams | dev_set_player_stat, dev_set_funzo_hp_frac, dev_clear_enemies, dev_spawn_custom, dev_force_grant_weapon, dev_remove_weapon, dev_reset_upgrades |
~120 | SimDevSeams. Keeps dev-only code out of the shipping conductor |
| Power-ups | consts + _collect_powerups, _apply_powerup |
~60 | Delete or PowerupSystem (§1) |
| Conductor proper | _init, tick (173), tick_single, movement of projectiles, XP/level-ups, checksum/snapshot |
~900 | stays |
The end state is about 1,000 lines. Also: sim.gd:808 has the only TODO in the codebase
(“integrate() still hardcoded to player/player2”). Solve that when co-op goes past 2 pilots, not in this pass.
main.gd: 2,896 lines, 93 funcs, 164 vars, 21% comments
Section titled “main.gd: 2,896 lines, 93 funcs, 164 vars, 21% comments”| Responsibility | Functions | ~Lines | Proposal |
|---|---|---|---|
| Run bootstrap | _new_run (545 lines, 79 .new() calls) |
545 | Split per NEXT_TASKS: _setup_sim_and_content, _setup_renderers (→ a RenderRig RefCounted that owns the ~40 renderer refs, which also drops ~40 vars from main), _setup_ui_and_hud, _setup_dev_overlays. That makes the run-start order unit-testable (the NEXT_TASKS remote-config item 3) |
| Per-frame render sync | _process (348), _physics_process (215) |
560 | Pull out the render-sync block (drain fx events → renderer syncs) into RenderRig.sync(sim, delta). The incident-narrative comments (e.g. the 25-line “corpse bug” story at _process +21..+81) belong in docs/architecture/rendering-and-performance.md |
| Enemy/projectile colouring (pure) | _build_enemy_type_colors (48), _base_color_for_type (32), _swarmer_color, _enemy_colors (44), _enemy_proj_colors, _proj_colors, _sync_aim_lance, + 6 colour buffers |
~220 | render/enemy_colorizer.gd (RefCounted, takes sim). Pure, so it’s headless-testable. Removes the dead primed branch |
| Dev console | apply_dev_command (53), _set_funzo_treatment, _dev_spawn_boss, _dev_spawn_positions, build_status, build_caps (47), save-debug label |
~220 | MainDevCommands (RefCounted, takes main) |
| Dev benches | _setup_showcase, _update_showcase_label, _showcase_dash_due + PERF_BENCH/MEASURE blocks |
~150 | Delete or DevBench (§2) |
| Level-up/dimension flow | _open_levelup (45), _next_leveling_pilot, _leveling_pilot_idx/_device, _await_drawn_frame, _on_upgrade_chosen, _open_dimension_choice, _on_dimension_chosen |
~180 | LevelUpFlow |
| Tutorial UI gating | tutorial_pause_hidden, tutorial_hotkey_blocked, tutorial_tip_for, _objective_panel_blocked, _touch_controls_blocked, _stage_tutorial_hud, _update_objective_panel |
~120 | TutorialUiGate |
| Menus/pause/bay/shop routing | _toggle_pause (69), _show_start_menu, _return_to_menu, bay, shop |
~300 | stays (this is main’s real job) |
ui/meta_shop_panel.gd: 1,739 lines, 81 funcs
Section titled “ui/meta_shop_panel.gd: 1,739 lines, 81 funcs”| Responsibility | Functions | ~Lines | Proposal |
|---|---|---|---|
| Pure layout maths (already static and tested) | layout_for, column_count, card_metrics, sort_for_grid, affordable_count, badge_count/text, content_width, sparse_row_x, tab_sections, fit_font_size, wrapped_line_count |
~170 | ShopLayout (static RefCounted). No behaviour change |
| Card factory + skins | _make_upgrade_card (65), _set_upgrade_card_content (59), _level_pips, _make_card, card_skin, _text_button_skin, _glass_pill, _cat_color, _cat_icon, _label, _header_button |
~260 | ShopCardFactory |
| Reset-shop dialog | _build_reset_dialog (72), _handle_reset_dialog_input, highlight/open/close/confirm |
~130 | ShopResetDialog (Control) |
| Tip overlay | _build_tip (52), _fill_tip_advice, _show_tip, _dismiss_tip, _handle_tip_input |
~130 | ShopTipOverlay (Control) |
| Grid/rail/nav/input | _ready (116), _input (101), _build_rail (92), _rebuild_grid, _move, focus |
~800 | stays |
4. Duplication
Section titled “4. Duplication”| Finding | Evidence | Conf. | Effort | Det. risk | Action |
|---|---|---|---|---|---|
cooldown_frac() identical in 11 weapons (+1 near-identical) |
clampf(1.0 - _timer / maxf(cooldown, 0.001), 0.0, 1.0) in 11 of 16 sim/weapon_*.gd that define it |
Certain | S | no | WeaponCooldown.frac(timer, cd) (NEXT_TASKS 2026-08-12) |
"power" mod-preview case |
32 "power": sites across sim/weapon_*.gd |
Likely | S–M | no | WeaponModDisplay.power_now_after() (NEXT_TASKS) |
| Menu nav debounce hand-rolled in 11 panels | 11 separate NAV_DEBOUNCE_MS consts (150/160/180/200), 41 debounce-timestamp sites. MenuNav only centralises the direction predicates. 7 separate _ui_nav() sound helpers |
Certain | M | no | Add MenuNav.Debouncer (or a static accept(ms)), with one tvOS value and per-panel override only where it’s justified (the shop’s 150 ms) |
| Boss death rewards ×5 | sim.gd:2635-2677 (see §3) |
Certain | S | no (keep the append order of fx_events/run_gold; neither is hashed) |
on_death() per director |
| Boss spawn/enrage boilerplate ×5 | Each spawn() does add() → state.reset() → max_hp → _boss_spawn_count += 1 → a name fx event. hp_frac + “latch ENRAGED” repeated in eye.gd:81-87, graviton.gd:69-77, funzo |
Certain | M | no, if the call order is kept | A small BossCommon.spawn_pooled(sim, spec) + BossCommon.check_enrage(state, hp, frac) |
| Boss stats: two sources of truth | Warden/Boss2/FunZo/Graviton use code consts (BOSS_HP etc.), Eye reads content.enemy("eye"). The bible values match today, but nothing enforces it |
Certain | S–M | no, while the values match | Pick one. Reading the bible also fixes summary #2 |
_spawn_story_wyrm vs _spawn_wyrm |
sim.gd:1162 (50) vs 1622 (82), same segment-building shape |
Likely | S | yes, if _spawn_wyrm’s draw order changes |
Share a builder; the story variant just skips scaling |
| Legacy vs glass level-up panels | level_up_panel.gd (788) + level_up_panel_legacy.gd (631) both implement nav/confirm/cards |
Certain | — | no | Delete legacy (§2) rather than dedupe |
| 6 enemy-attack timer passes rebuild dicts each tick | enemy_attacks.gd vs update_lancers (NEXT_TASKS 2026-08-12 perf) |
Likely | S | no (dictionary iteration order: check that nothing iterates these for RNG) | Mirror update_lancers |
| Duplicate sprite assets | render/ship_sprites/nova/hull_b_{body,glow}.png = manta_{body,glow}.png (same md5) |
Certain | S | no | Keep one and point both hull ids at it |
5. Test suite health
Section titled “5. Test suite health”Measured run: 80.8 s total, 3151 tests, 0 failing, 4 risky, 163 orphans. The suite is healthy and fast. The findings are about dead coverage, not speed.
| Finding | Evidence | Conf. | Effort | Det. risk | Action |
|---|---|---|---|---|---|
| Slowest scripts | test_soak_run 18.4 s (29 tests), test_economy_career 11.3 s (4 tests, 3.8 s each), test_profiled_auto_player 8.9 s, test_main 7.1 s, test_testflight_build33 4.5 s. The top 3 are 48% of the suite |
Certain | S | no | Acceptable. If gate time matters, shorten the career tests’ run count, or tag them “slow” and run them in gate.sh only |
| Tests of dead behaviour | 4× *_death_advances_next_boss_time (boss, eye, funzo, graviton); test_areas swarm-burst case; test_elemental.gd (whole file, Elemental.apply); reaction cases in test_mods_in_sim/test_content_db; test_threat_radar.gd (V1 radar, flips radar_v2 off); test_level_up_panel_legacy.gd (168); legacy spider-ray tests (test_elite_powers.gd:126+); Boss2 tests (test_boss2*.gd, 232 lines, feature disabled) |
Certain | S | no | Delete alongside the code they cover |
Flag-conditional pass_test escapes |
15+ sites where a test passes trivially depending on a const: test_player_renderer.gd:143-195 (6), test_main.gd:1184,1202 (legacy panel), test_survival_ruleset.gd:119,165, test_upgrade_effects.gd:97,103 (“no reaction_damage_mult mod in content”) |
Certain | S | no | Once the flags are inlined these become real asserts or get deleted. Today they can hide regressions |
| 4 risky (no-assert) tests | test_debug_settings_panel: test_apply_background_variant_is_null_safe, test_apply_graphics_toggle_is_null_safe, test_apply_tilt_mode_is_null_safe; test_player_renderer: test_set_tilt_is_safe_to_call_in_every_mode_without_crashing |
Certain | S | no | Add pass_test("no crash") or a real assert |
| 163 orphan nodes | spread over about 40 scripts, 1–4 each | Certain | M | no | Low priority; autofree/add_child_autofree |
| Build-numbered / naming-drift tests | test_testflight_build33.gd (fine as regression, but name it by behaviour); test_crystal_state.gd, test_crystal_endless_tuning.gd, test_dud_crystal_filter.gd; the determinism test’s assert messages say “crystals mode” / “crystals snapshot hash baseline” (test_determinism_survival.gd) |
Certain | S | no (messages only, don’t touch the pinned numbers) | Rename files and messages |
| Test files larger than their subject | test_dimension_hazards 1106 vs 723; test_debug_settings_panel 824 vs 746; test_funzo 531 vs 316; test_graviton 484 vs 224; test_eye 514 vs 306; test_main 1497 |
Certain | — | no | Not a problem in itself; the boss tests shrink when the dead _next_boss_time tests go |
6. tools/ and scripts/
Section titled “6. tools/ and scripts/”| Item | Status | Evidence | Conf. | Action |
|---|---|---|---|---|
scripts/verify-sim-refactor.sh |
Dead: hardcoded BASELINE_SCRIPTS=180, BASELINE_TESTS=1231 (the suite is 320/3151), one-off from the July split |
last touched 2026-07-02, 1 ref | Certain | Delete (gate.sh + check-test-count.sh replace it) |
scripts/deploy-demo.sh |
Live (bh-deploy skill uses it) | last touched 2026-06-23 | Likely | Keep |
gate.sh, check-test-count.sh, soak.sh, ui-sweep.sh, economy-careers.sh, publish-config.mjs, release/, fix-tvos-appstore-icons.py, test/ |
Live | 5–72 refs each, touched Sep | Certain | Keep. Note that gate.sh runs git checkout -- project.godot on exit, which silently discards any intended project.godot edit in the working tree |
tools/glow_bake/ (32 files) |
Mostly one-off 2026-07-22 experiments. bake_ghost_glow/bake_orbiter_glow/compare_ghost_glow/bake_hires write outputs that were never committed. live_check_warden_enraged.tscn, live_check_warden_untinted.tscn have 0 refs; verify_start_menu_carousel.tscn has nothing to do with glow |
USE_BAKED_GLOW := false |
Likely | Keep bake_shape_glows (its output is live in the renderer) and preview; delete the rest, or move them to a tools/_experiments/ with .gdignore |
tools/glow_compare, nova_vfx, nova_units, look_metrics, warp_capture, glass_preview |
One-off A/B harnesses from the 2026-09-23/24 look goal (1–6 refs, mostly docs). bh-vfx-iteration references the pattern |
Likely | Keep glow_compare (the skill uses it); archive the rest once the look A/Bs are retired (§2) |
|
tools/app_icon |
One-off, but reusable for icon refreshes | Likely | Keep | |
tools/economy_sim/ablate.gd, calibrate_runner.gd, tools/sim_bench/sim_bench.gd |
Entry points run via godot -s, not referenced by other code. Fine; documented in docs/research/2026-09-27-* and rendering-and-performance.md |
Certain | Keep | |
tools/shop_lab |
Last touched 2026-07-02; the shop was rebuilt on liquid glass on 2026-09-24 | Guessing | Check that it still boots. If not, delete | |
tools/story_board |
Live (dev-tools doc), tutorial-only (NEXT_TASKS) | Certain | Keep | |
| Missing paths referenced | Only tool output paths (.godot/imported/*, tools/*/out*.png, the two unbaked glow PNGs). No live-code res:// path is missing |
Certain | None needed |
7. Repo hygiene
Section titled “7. Repo hygiene”| Finding | Evidence | Conf. | Effort | Action |
|---|---|---|---|---|
| Byte-identical music shipped twice | audio/menu_theme.mp3 and audio/survival_loop.mp3, both 3,737,616 B, same md5. Both are played (main.gd:478,728) |
Certain | S | Either one file for both events (saves 3.7 MB of bundle), or the survival loop was meant to be different music: ask Chris |
| Stale worktrees | 5 under .claude/worktrees/ (about 385 MB). See summary #3: 3 unmerged commits on worktree-agent-a0a9… (+2 dirty files: main.gd, ui/stats_sheet.gd) and -a46b…; networked-multiplayer-mb-mc is 54 commits ahead (2026-07-03, netcode work; probably belongs with ~/Claude/sovereign or an archive tag) |
Certain | S | Diff each unmerged commit against main. Tag networked-multiplayer-mb-mc as archive/netcode-mb-mc before removing it. Then git worktree remove + git branch -D |
| Stale branches | tutorial-lessons and origin/feature/8-dimensions-full are both fully merged into main |
Certain | S | git branch -d tutorial-lessons; git push origin --delete feature/8-dimensions-full (outward-facing, so it needs a same-day OK) |
_archive/ |
see §1 | Certain | S | Delete (tags exist) or .gdignore it |
| Important gitignored files | .claude/skills/* (19 project skills incl. bh-deploy, bh-appstore-release, bh-determinism-repin) are ignored by the global ~/.gitignore_global:13 .claude/, so Toby doesn’t get them and they have no history. .claude/hooks/, the root export_presets.cfg (ignored at .gitignore:6; the tvOS one is tracked). .claude/skills/bh-deploy-mac-dev.md is tracked but is a flat .md, not a <name>/SKILL.md dir, so it isn’t a loadable skill |
Certain | S–M | Chris’s call: force-add the skills dir (the rescue-gitignored-content pattern), which matters now there are two devs |
| Largest tracked binaries | marketing/audio/dark_cosmos_theme.wav 4.9 MB, 3 × music mp3 ~3.7 MB, audio/orbit_ambience.wav 0.9 MB, App Store PNGs 0.4–1 MB. Pack is 57 MB total, so it’s fine |
Certain | — | Optionally convert orbit_ambience.wav to ogg |
audio/candidates/ (12 files, 264 KB) tracked |
Sound-map audition candidates; blaster_fire.wav = candidates/weapon.blaster.fire/c1.wav. The tvOS export doesn’t exclude audio/candidates/* |
Likely | S | Add to the export exclude_filter, or untrack |
| Export filters | The tvOS preset excludes tests/*, telemetry/*, tools/* but not _archive/*, docs/*, audio/candidates/*. The root presets exclude only marketing/*, so tests and tools may ship in the web/mac export |
Likely | S | Align the exclude filters across presets |
Orphan .uid files on disk |
17 (§1) | Certain | S | Delete |
| TODO/FIXME/HACK | 1 in all of .gd (sim.gd:808). Legacy/temporary markers: main.gd 7, meta_store.gd 3, level_up_base.gd 2, pause_menu.gd 2, shop_categories.gd 2 |
Certain | — | Deferred work is tracked in NEXT_TASKS rather than in inline TODOs; the “TEMPORARY”/“SPIKE” markers in main.gd line up with §2 |
8. Doc drift
Section titled “8. Doc drift”| Doc | Says | Reality | Conf. | Action |
|---|---|---|---|---|
CLAUDE.md |
sim.gd is “~2,400-line conductor” |
3,053 lines | Certain | Update after the §3 split (or now) |
CLAUDE.md |
RNG streams at sim/sim.gd:245-248 |
sim.gd:273-276 |
Certain | Replace the line numbers with the symbol names |
CLAUDE.md |
_damage_enemy/_resolve_collisions/_apply_element live in elemental_system.gd |
They’re damage_enemy/resolve_collisions/apply_element (no underscore) |
Certain | Fix the names |
CLAUDE.md |
boss_rotation.gd = “boss-index lookups + survival rotation dispatch” |
Rotation dispatch removed 2026-07-09 (boss_rotation.gd:5) |
Certain | Fix |
CLAUDE.md |
“the seed-1234 600-tick baseline is pinned in tests/test_determinism_survival.gd” |
There are 3 determinism files (test_determinism.gd, _survival, _coop) |
Certain | List all three |
docs/architecture/enemies-bosses-survival.md:51 |
_maybe_spawn_survival_boss() dispatches a 5-boss rotation |
Removed; FunZo only (V01_FUNZO_ONLY) |
Certain | Rewrite as current state |
…enemies-bosses-survival.md:70-72 |
Swarm crescendo at 3:00; first boss at BOSS_FIRST_TIME 210 s, BOSS_INTERVAL 80 s cadence |
Burst has no caller; boss timing is phase-driven (WAVE_PHASE_S → BOSS_PREP_S), so both consts are dead (remote-config.md:169-171 already says this, so the two docs contradict each other) |
Certain | Rewrite |
…enemies-bosses-survival.md:48,72 |
_spawn_suppressed, _any_boss_alive() |
Neither function exists | Certain | Remove |
docs/architecture/dev-tools.md:18 |
_spawn_boss() has no positioned overload |
No _spawn_boss() exists |
Certain | Update to _dev_spawn_boss → director spawn() |
docs/architecture/rendering-and-performance.md |
main.gd._enemy_scales |
Doesn’t exist | Certain | Update |
sim/beacon_director.gd:8-9 header |
aura “shrink[s] its flank/backstab jitter (Sim._move_enemies)” |
Not implemented (summary #10) | Certain | Wire it or fix the comment |
sim.gd:2698-2702 comment |
_spawn_teaser_wormhole “retired”, its machinery “left in place” |
Accurate, but the leftover machinery still ticks every frame (§1) | Certain | Delete the machinery |
main.gd:158 comment |
“adaptive quality runs” | ADAPTIVE_QUALITY := false |
Certain | Fix/delete |
sim.gd:615 comment |
“5-way rotation counter” | Write-only counter, no rotation | Certain | Delete the field |
NEXT_TASKS.md (co-op Leech, 2026-08-12) |
“Leech heal (sim/sim.gd:2450) always targets player.hp (P1)” |
Fixed 2026-09-23: heals every living pilot (sim.gd:2567-2570, main.gd:196 comment) |
Certain | Mark the heal half DONE; the shared-ModState design question stays open |
NEXT_TASKS.md (playtest batch 2) |
dimension-choice spec “is currently UNTRACKED” | It’s tracked (git ls-files shows docs/superpowers/specs/2026-07-09-dimension-choice-popup-design.md) |
Certain | Strike that sentence |
NEXT_TASKS.md (2026-07-16) |
snapshot_string() at sim/sim.gd:2800 |
sim.gd:3035 |
Certain | Use the symbol name |
Memory bullet-heaven-measurement-open-loop |
“ADAPTIVE_QUALITY off; re-enable at release” | 1.0 submitted with it off | Certain | Update memory / make the decision |
NEXT_TASKS “## Open” overlap
Section titled “NEXT_TASKS “## Open” overlap”| NEXT_TASKS item | Overlap with this audit |
|---|---|
| 2026-09-27 dead sim pacing fields | Same as §1 / summary #4. This audit adds _boss_spawn_count (write-only), _spawn_accum and other_area() |
| 2026-09-27 dead bible fields | Same as §1 xp_val. This audit adds boss HP/speed/contact for warden/funzo/graviton/boss2, which are unread yet patchable (summary #2) |
2026-08-12 primed/react_cd + main.gd tint branch |
Same as §1 / summary #5 |
2026-08-12 Elemental.apply() + ContentDB.reaction() |
Same as §1 |
2026-08-12 _new_run 510-line split |
Same as §3 main.gd (now 545 lines) |
2026-08-12 cooldown_frac / power preview duplication |
Same as §4 |
2026-08-12 perf: enemy_attacks dict rebuild, fx_manager._spawn dicts, weapon_panel views |
§4 lists the enemy_attacks one. The others are perf, outside this audit’s scope |
| 2026-08-12 co-op SimMods/Leech | §8: the heal half is now stale (fixed); the design half is open |
2026-09-27 remote-config item 3 (no e2e test of _new_run order) |
Unblocked by the §3 _new_run extraction |
| 2026-09-27 remote-config “unverified-dead meta magnitudes” | Same class as summary #2. Add the boss-stat fields to that check |
| 2026-07-09/10 dimension-choice spec “untracked” | §8: stale, it’s committed |
| 2026-07-16 per-tick determinism trace tool | Would de-risk the §3 SpawnSystem extraction. Worth building first |
| Everything else (ui-sweep guard, sweep sRGB, MetaState.from_dict, P2 meta stats, story_board, Toby-era tvOS flake) | No overlap |
Suggested order for the post-1.0 pass
Section titled “Suggested order for the post-1.0 pass”- Bugs first (not cleanup): Halo Chain/splitter biomass (#1) and the unread boss-stat remote-config fields (#2).
- Zero-risk deletions in one PR: §1 dead symbols, dead const-flag branches (§2 rows marked
“Dead path”), the dead tests in §5,
verify-sim-refactor.sh, orphan.uids,_archive/. Gate:scripts/gate.sh. All three determinism tests must stay green without a re-pin. If one moves, something wasn’t dead. - Chris’s calls, batched into one question list: power-ups, Boss2,
ADAPTIVE_QUALITY, eachVisualLook/radar_v2A/B pick, the duplicate music file, committing.claude/skills. - Structural: the
EnemyColorizer/MainDevCommands/ShopLayoutextractions (low risk), then thesim.gdsplit one module per PR, with the per-tick trace tool built first.