Codebase Audit: Data, Constants & Types Analysis #67

Closed
opened 2026-05-19 08:42:59 +02:00 by Anexim · 3 comments
Owner

<longcat_arg_value>Comprehensive audit of all files in data/, constants/, and types/ directories. Checking for duplicates, conflicts, type mismatches, banned content, missing entries, naming inconsistencies, data integrity issues, and balance problems.

<longcat_arg_value>Comprehensive audit of all files in data/, constants/, and types/ directories. Checking for duplicates, conflicts, type mismatches, banned content, missing entries, naming inconsistencies, data integrity issues, and balance problems.
Anexim added the ai:in-progress label 2026-05-19 08:42:59 +02:00
n8n-gitea was assigned by Anexim 2026-05-19 08:42:59 +02:00
Author
Owner

<longcat_arg_value>## Comprehensive Codebase Audit Report

I read and analyzed all 50+ files across data/, constants/, and types/ directories. Here are all findings organized by category.


1. DUPLICATE / CONFLICTING DEFINITIONS

[ISSUE 1] EquipmentSlot defined in TWO places with potential for drift

  • src/lib/game/types/equipmentSlot.ts (line 3-10) — canonical type: 'mainHand' | 'offHand' | 'head' | 'body' | 'hands' | 'feet' | 'accessory1' | 'accessory2'
  • src/lib/game/data/equipment/types.ts (line 3) — re-exports from equipmentSlot.ts via export type { EquipmentSlot } from '../../types/equipmentSlot'
  • Severity: 2 — Currently consistent, but the re-export pattern means data/equipment/types.ts has a redundant re-export. The EQUIPMENT_SLOTS array in data/equipment/types.ts (line 6-7) manually lists the same values. If one changes without the other, they'll drift.
  • Fix: Remove the manual EQUIPMENT_SLOTS array and derive it from a shared source, or add a compile-time assertion.

[ISSUE 2] rawCost and elemCost helper functions defined in THREE places

  • src/lib/game/constants/elements.ts (lines 7-14) — used by spell definitions
  • src/lib/game/data/golems/types.ts (lines 7-13) — duplicate implementation
  • src/lib/game/types/spells.tsSpellCost interface defines the same shape
  • Severity: 3 — The golem types file has its own copy of rawCost/elemCost that could diverge from the canonical versions in constants/elements.ts.
  • Fix: Import from constants/elements.ts in golem types instead of redefining.

[ISSUE 3] SPELL_EFFECTS in data/enchantments/spell-effects/index.ts re-exports EnchantmentEffectDef from ../../enchantment-types but the index.ts at data/enchantments/index.ts also re-exports the same type from ../enchantment-types. The spell-effects sub-index re-exports it as a type-only export which is fine, but creates two import paths to the same type.

  • Severity: 1 — Cosmetic, but adds confusion.

[ISSUE 4] ALL_CASTER constant defined in data/enchantments/spell-effects/types.ts (line 7) is a local ['caster'] array. Meanwhile data/enchantments/combat-effects.ts defines its own CASTER_AND_HANDS locally. These are separate constants for overlapping concepts with no shared source.

  • Severity: 2 — Minor inconsistency, could lead to drift.

2. MISSING EXPORTS / BROKEN RE-EXPORTS

[ISSUE 5] types/index.ts does NOT export DisciplinesAttunementType, PerkType, DisciplinePerk, DisciplineDefinition, DisciplineState, MAX_CONCURRENT_DISCIPLINES, or BASE_CONCURRENT_DISCIPLINES from types/disciplines.ts

  • src/lib/game/types/index.ts (line 1) — only re-exports from elements, attunements, spells, equipment, equipmentSlot, and game
  • Severity: 3 — Any code needing discipline types must import directly from types/disciplines rather than the barrel export, breaking the pattern used by all other type files.
  • Fix: Add export type { DisciplinesAttunementType, PerkType, DisciplinePerk, DisciplineDefinition, DisciplineState } from './disciplines' and export { MAX_CONCURRENT_DISCIPLINES, BASE_CONCURRENT_DISCIPLINES } from './disciplines' to types/index.ts.

[ISSUE 6] types/index.ts does NOT export GolemDef, GolemManaCost from data/golems/types.ts — but these aren't in the types/ directory, they're in data/golems/types.ts. The types/index.ts barrel doesn't cover data types at all.

  • Severity: 2 — Inconsistent barrel pattern. Data types are not re-exported through any central barrel.

[ISSUE 7] constants/index.ts does NOT export ELEMENT_OPPOSITES, ELEMENT_ICON_NAMES, or BASE_UNLOCKED_ELEMENTS from elements.ts — wait, actually it does re-export them (line 7). However, FLOOR_ELEM_CYCLE is NOT exported from the barrel.

  • src/lib/game/constants/index.ts (line 6) — exports ELEMENTS and FLOOR_ELEM_CYCLE is missing from the re-export list.
  • Severity: 2 — Code needing FLOOR_ELEM_CYCLE must import directly from constants/elements.

3. TYPE MISMATCHES

[ISSUE 8] SpellEffect.type in types/spells.ts (line 33) includes 'shield' and 'buff' as effect types, but these are never used in any spell definition across all spell module files. Conversely, spell definitions use effect types 'chain', 'aoe', 'armor_pierce' which ARE used but the type definition does include 'armor_pierce' — however 'chain', 'cast_speed', 'crit_chance', 'crit_damage', 'raw_damage', 'void_resist', 'all_damage' used in guardian effects are NOT in the SpellEffect.type union.

  • src/lib/game/types/spells.ts (line 33): type: 'burn' | 'freeze' | 'stun' | 'pierce' | 'multicast' | 'shield' | 'buff' | 'chain' | 'aoe' | 'armor_pierce'
  • Guardian effects in constants/guardians.ts use: 'burn', 'armor_pierce', 'cast_speed', 'crit_chance', 'crit_damage', 'raw_damage', 'void_resist', 'all_damage'
  • Severity: 3 — Guardian effects use types 'cast_speed', 'crit_chance', 'crit_damage', 'raw_damage', 'void_resist', 'all_damage' that are NOT in the SpellEffect.type union. If guardian effects are ever validated against this type, they'll fail.
  • Fix: Add missing types to the SpellEffect.type union.

[ISSUE 9] GuardianDef.effects in types/attunements.ts (line 56) is typed as { type: string; value: number }[] — using string instead of a union type. This is a weak type that bypasses type checking.

  • Severity: 2 — Should use the same union as SpellEffect.type or a shared effect type.

[ISSUE 10] GolemDef in data/golems/types.ts has baseManaType: string and armorPierce: number fields. The GolemManaCost interface in the same file duplicates the shape of SpellCost from types/spells.ts but with element?: string instead of the more specific typing.

  • data/golems/types.ts (lines 15-19): GolemManaCost = { type: 'raw' | 'element'; element?: string; amount: number }
  • types/spells.ts (lines 5-9): SpellCost = { type: 'raw' | 'element'; element?: string; amount: number }
  • Severity: 2 — These are structurally identical but independently defined. Changes to one won't reflect in the other.

[ISSUE 11] AchievementDef.reward in types/game.ts (lines 53-59) has unlockEffect?: string but the type doesn't constrain what values are valid. The masterEnchanter achievement uses unlockEffect: 'efficiencyBoost' but there's no enum or union for valid effect IDs.

  • Severity: 1 — Minor, but could lead to typos going undetected.

4. HARDCODED VALUES THAT SHOULD BE DERIVED

[ISSUE 12] FLOOR_ELEM_CYCLE in constants/elements.ts (line 76) is hardcoded as ["fire", "water", "air", "earth", "light", "dark", "death"] — this should be derived from the ELEMENTS record's base category entries.

  • Severity: 2 — If a base element is added/removed from ELEMENTS, the cycle won't update automatically.

[ISSUE 13] BASE_UNLOCKED_ELEMENTS in constants/elements.ts (line 79) is hardcoded as ['transference'] — should be derived from ELEMENTS entries where cat === 'utility' or similar logic.

  • Severity: 1 — Minor, but same derivation issue.

[ISSUE 14] MAX_ATTUNEMENT_LEVEL = 10 in data/attunements.ts (line 196) is hardcoded. The getAttunementXPForLevel function has a comment describing scaling up to level 5 but the max is 10 with no XP formula beyond level 5.

  • Severity: 2 — The XP formula Math.floor(1000 * Math.pow(2, level - 2) * (level >= 3 ? 1.25 : 1)) grows exponentially and will produce enormous numbers for levels 6-10.

[ISSUE 15] getAttunementXPForLevel in data/attunements.ts (lines 178-186) has a broken formula. The comment says "each level requires 2x the previous, starting from 1000" but the code does 1000 * Math.pow(2, level - 2) * 1.25 for level 3+, which means:

  • Level 3: 1000 * 2^1 * 1.25 = 2500 ✓ (matches comment)
  • Level 4: 1000 * 2^2 * 1.25 = 5000 ✓ (matches comment)
  • Level 5: 1000 * 2^3 * 1.25 = 10000 ✓ (matches comment)
  • Level 6: 1000 * 2^4 * 1.25 = 20000 — but the comment says "etc." implying this continues
  • Level 10: 1000 * 2^8 * 1.25 = 320,000 — extremely steep
  • Severity: 2 — The formula works but the comment is misleading about the scaling pattern.

5. MISSING ENTRIES IN ARRAYS/RECORDS

[ISSUE 16] ELEMENT_OPPOSITES in constants/elements.ts (lines 68-72) is incomplete. It covers fire/water, air/earth, light/dark, and lightning/earth, but is missing opposites for:

  • death — no opposite defined
  • metal — no opposite defined
  • sand — no opposite defined
  • crystal — no opposite defined
  • stellar — no opposite defined
  • void — no opposite defined
  • transference — no opposite defined
  • Severity: 3 — If the damage calculation uses opposites, composite/exotic elements have no defined opposite, which could cause runtime errors or undefined behavior.

[ISSUE 17] ELEMENT_ICON_NAMES in constants/elements.ts (lines 74-79) is missing entries for composite and exotic elements. It has entries for all base + utility + composite + exotic elements (15 entries for 15 elements). Actually, checking: fire, water, air, earth, light, dark, death, transference, metal, sand, lightning, crystal, stellar, void, raw = 15 entries for 15 ELEMENTS entries. This is complete. ✓

[ISSUE 18] GUARDIANS in constants/guardians.ts has entries for floors 10, 20, 30, 40, 50, 60, 80, 90, 100 — but floor 70 is MISSING. The sequence jumps from 60 to 80.

  • Severity: 4 — Floor 70 will have no guardian defined. If the game tries to access GUARDIANS[70], it gets undefined, which will cause a runtime crash.
  • Fix: Add a guardian definition for floor 70.

[ISSUE 19] CRAFTING_RECIPES in data/crafting-recipes.ts references equipment types like 'oakStaff', 'crystalWand', 'scholarRobe', 'arcanistStaff', 'battlestaff', 'fireCatalyst', 'runicShield', 'wizardHat', 'spellweaveGloves', 'travelerBoots', 'silverRing', 'silverAmulet'. Cross-referencing with EQUIPMENT_TYPES:

  • oakStaff → ✓ exists in casters.ts
  • crystalWand → ✓ exists in casters.ts
  • scholarRobe → ✓ exists in body.ts
  • arcanistStaff → ✓ exists in casters.ts
  • battlestaff → ✓ exists in casters.ts
  • fireCatalyst → ✓ exists in catalysts.ts
  • runicShield → ✓ exists in shields.ts
  • wizardHat → ✓ exists in head.ts
  • spellweaveGloves → ✓ exists in hands.ts
  • travelerBoots → ✓ exists in feet.ts
  • silverRing → ✓ exists in accessories.ts
  • silverAmulet → ✓ exists in accessories.ts
  • All recipe equipment type references are valid. ✓

[ISSUE 20] Spell effect enchantments reference spell IDs via spellId in their effect field. Cross-referencing a sample:

  • spell_manaBoltspellId: 'manaBolt' → ✓ exists in RAW_SPELLS
  • spell_fireballspellId: 'fireball' → ✓ exists in BASIC_ELEMENTAL_SPELLS
  • spell_stellarNovaspellId: 'stellarNova' → ✓ exists in LEGENDARY_SPELLS
  • All spell effect enchantments reference valid spell IDs. ✓

[ISSUE 21] DEFENSE_EFFECTS in data/enchantments/defense-effects.ts is an EMPTY record {}. The file comment says "Currently empty - no defense effects defined."

  • Severity: 2 — Having an entire category with zero effects suggests incomplete implementation. The EnchantmentEffectCategory type includes 'defense' as a valid category, but no effects exist for it.

6. INCONSISTENT NAMING CONVENTIONS

[ISSUE 22] Inconsistent spell naming: some use camelCase keys (fireball, waterJet, stoneBullet), but the AOE variants use a different pattern: fireballAoe (lowercase 'aoe') vs earthquakeAoe. The pattern is inconsistent — fireballAoe appends 'Aoe' while earthquakeAoe also appends 'aoe'. However, meteorShower and blizzard don't use the Aoe suffix despite being AOE spells.

  • src/lib/game/constants/spells-modules/aoe-spells.tsfireballAoe, frostNova, meteorShower, blizzard, earthquakeAoe, apocalypse
  • Severity: 2 — Inconsistent naming: some AOE spells have Aoe suffix, others don't.

[ISSUE 23] Inconsistent enchantment effect ID naming:

  • Combat effects use damage_5, damage_10, damage_pct_10, crit_5, attack_speed_10 (snake_case with numbers)
  • Mana effects use mana_cap_50, mana_regen_1, click_mana_1 (snake_case with numbers)
  • Elemental effects use sword_fire, sword_frost (snake_case)
  • Special effects use spell_echo_10, guardian_dmg_10 (snake_case with numbers)
  • Utility effects use meditate_10, study_10, insight_5 (snake_case with numbers)
  • Spell effects use spell_manaBolt, spell_fireball (snake_case with camelCase spell IDs)
  • Severity: 2 — The naming is internally consistent within categories but varies across categories. The spell effects prefix with spell_ while others don't use category prefixes.

[ISSUE 24] EquipmentType in data/equipment/types.ts uses baseDamage and baseCastSpeed for swords, but SpellDef in types/spells.ts uses dmg (not baseDamage) and castSpeed (not baseCastSpeed). Different naming for the same concepts.

  • Severity: 2baseDamage vs dmg, baseCastSpeed vs castSpeed — inconsistent naming for equivalent concepts.

[ISSUE 25] AttunementDef uses primaryManaType (optional, can be undefined for Invoker) while GolemDef uses baseManaType (required string). Different prefix for the same concept ("primary" vs "base").

  • Severity: 1 — Minor naming inconsistency.

7. BANNED CONTENT CHECK

[ISSUE 26] No lifesteal or healing found. The Drain spell (death element, Siphon vital energy) and Mana Rip / Essence Drain (transference element, Rip mana from the enemy) deal damage — they don't heal the player. The adrenaline_rush special effect restores mana (not HP) on enemy defeat. No banned healing mechanics detected.

[ISSUE 27] No banned mana types found. Scanned all files for life, blood, wood, mental, force as mana types. The constants/elements.ts file explicitly notes: "Life, Blood, Wood, Mental, and Force mana types have been removed." The ManaType union in types/element.ts contains only: raw, fire, water, air, earth, light, dark, death, transference, metal, sand, lightning, crystal, stellar, void — all valid.

[ISSUE 28] ⚠️ vitalityEssenceDrop in data/loot-drops.ts (lines 103-109) — "Vitality Essence" could be confused with a life/healing mechanic, but it's typed as 'essence' and grants elemental mana. The name is suspicious but the implementation doesn't violate banned content rules.

  • Severity: 1 — Consider renaming to avoid confusion with banned life/healing concepts.

8. MISSING OR INCORRECT TYPE ANNOTATIONS

[ISSUE 29] DisciplineDefinition.attunement field in types/disciplines.ts (line 37) is typed as DisciplinesAttunementType (the enum), but the discipline data files (base.ts, enchanter.ts, etc.) use string literals like 'base', 'enchanter', 'fabricator', 'invoker'. The enum values match these strings, so this works, but the enum is a const enum-like pattern using enum with string values. This is actually correct since TypeScript string enums accept their defined values. ✓

[ISSUE 30] getUnlockedAttunements in data/attunements.ts (line 93) returns AttunementDef[] but uses .filter(Boolean) on the result of .map() which can include undefined values (when ATTUNEMENTS_DEF[id] doesn't exist). The return type claims it's AttunementDef[] but the .filter(Boolean) is needed precisely because undefined is possible. The type signature hides this.

  • Severity: 2 — The function signature should acknowledge the potential for missing attunements, or the input should be constrained.

[ISSUE 31] getAvailableDrops in data/loot-drops.ts (line 176) returns LootDrop[] but the filter uses drop.minFloor > floor which means drops with minFloor === floor are included. This is correct (≥), but the function name "available" could be clearer.

  • Severity: 1 — Minor semantic issue.

[ISSUE 32] CraftingRecipe in data/crafting-recipes.ts (line 7) imports EquipmentSlot from ./equipment/types but doesn't use it in the interface. The CraftingRecipe interface has no slot field — the slot is determined by the resulting equipment type.

  • Severity: 1 — Unused import (though this may be cleaned up by the compiler with isolatedModules).

9. DATA INTEGRITY ISSUES

[ISSUE 33] GUARDIANS floor 70 is MISSING (see ISSUE 18 above). This is the most critical data integrity issue.

[ISSUE 34] GUARDIANS floor 80 guardian (Mors Ultima) has element: "death" but unlocksMana: ['death'] — it only unlocks its own element. Meanwhile floor 90 (Primordialis, void) unlocks ['void', 'stellar'] and floor 100 (The Awakened One, stellar) only unlocks ['stellar']. The pattern is inconsistent — some guardians unlock 1 mana type, others unlock 2.

  • Severity: 2 — Not a bug, but an inconsistency that could confuse players.

[ISSUE 35] enchantment-effects.ts (line 1) re-exports everything from enchantments/index, which in turn imports from spell-effects/index. The spell-effects/index.ts imports from types.ts which re-exports EnchantmentEffectDef from ../../enchantment-types. This creates a re-export chain: enchantment-effects.tsenchantments/index.tsspell-effects/index.tsspell-effects/types.tsenchantment-types.ts. The type EnchantmentEffectDef is available through multiple import paths.

  • Severity: 1 — Not a bug, but the re-export chain is deep and could be simplified.

[ISSUE 36] data/equipment/types.ts (line 3) re-exports EquipmentSlot from ../../types/equipmentSlot, but types/index.ts also re-exportes it from ./equipmentSlot. Code can import EquipmentSlot from either data/equipment/types or types/index — two canonical paths.

  • Severity: 1 — Minor, but could cause confusion about which import path to use.

10. BALANCE ISSUES

[ISSUE 37] Mana Bolt (tier 0) has dmg: 5 at cost: rawCost(3) with castSpeed: 3 → DPS = 15, mana cost per second = 9. Ember Shot (tier 1) has dmg: 10 at cost: elemCost("fire", 1) with castSpeed: 3 → DPS = 30, mana cost per second = 3. Ember Shot is 2x the DPS at 1/3 the mana cost of Mana Bolt. Tier 1 basic spells dramatically outclass tier 0 raw spells, which is expected progression, but the gap is very large.

  • Severity: 2 — Expected progression, but the tier 0 spells may never be used.

[ISSUE 38] Apocalypse (tier 3 AOE) has dmg: 80 at cost: elemCost("fire", 20) with castSpeed: 0.5 hitting 10 targets. Total DPS = 400, mana cost per second = 10. Compare to Pyroclasm (tier 3 single target): dmg: 250 at cost: elemCost("fire", 25) with castSpeed: 0.6 → DPS = 150, mana cost per second = 15. Apocalypse is both higher DPS AND more mana efficient while hitting 10 targets. This makes single-target tier 3 spells obsolete.

  • Severity: 3 — AOE spells being strictly better than single-target spells at the same tier is a balance problem.

[ISSUE 39] Crystal Shatter (tier 4, crystal) costs elemCost("crystal", 10) for dmg: 400 at castSpeed: 0.5 → DPS = 200, mana cost per second = 5. Void Collapse (tier 4, void) costs elemCost("void", 12) for dmg: 450 at castSpeed: 0.45 → DPS = 202.5, mana cost per second = 5.4. Stellar Nova (tier 4, stellar) costs elemCost("stellar", 15) for dmg: 500 at castSpeed: 0.4 → DPS = 200, mana cost per second = 6. All three legendary spells have nearly identical DPS/mana efficiency (~40 DPS per mana/sec), which is actually well-balanced. ✓

[ISSUE 40] getGolemMaintenanceMultiplier in data/golems/utils.ts (line 107) returns 1 - (skills.golemSiphon || 0) * 0.1. At skill level 10, this returns 0 (free maintenance). At skill level 11+, it goes NEGATIVE (the game would pay you to maintain golems). There's no floor clamp.

  • Severity: 3 — Missing Math.max(0, ...) clamp. High skill levels could cause negative maintenance costs, effectively generating mana.

[ISSUE 41] getStudyCostMultiplier in constants/core.ts (line 25) returns 1 - (skills.focusedMind || 0) * 0.05. At skill level 20+, this goes NEGATIVE (negative study costs). No floor clamp.

  • Severity: 3 — Same issue. Should be Math.max(0, ...) or similar.

[ISSUE 42] getStudySpeedMultiplier in constants/core.ts (line 21) returns 1 + (skills.quickLearner || 0) * 0.1. No upper bound — at skill level 100, study speed is 11x. This may be intentional but could cause balance issues.

  • Severity: 2 — No cap on study speed multiplier.

SUMMARY TABLE

# Category Severity File(s) Description
1 Duplicate 2 equipmentSlot.ts, equipment/types.ts EquipmentSlot defined in two places
2 Duplicate 3 elements.ts, golems/types.ts rawCost/elemCost duplicated
3 Duplicate 1 enchantments/index.ts, spell-effects/index.ts Dual re-export paths for EnchantmentEffectDef
4 Duplicate 2 spell-effects/types.ts, combat-effects.ts Overlapping constants for equipment categories
5 Missing Export 3 types/index.ts Discipline types not in barrel export
6 Missing Export 2 types/index.ts Data types not covered by barrel
7 Missing Export 2 constants/index.ts FLOOR_ELEM_CYCLE not re-exported
8 Type Mismatch 3 spells.ts, guardians.ts Guardian effect types not in SpellEffect.type union
9 Type Mismatch 2 attunements.ts GuardianDef.effects uses weak string type
10 Type Mismatch 2 golems/types.ts, spells.ts GolemManaCost duplicates SpellCost shape
11 Type Mismatch 1 game.ts unlockEffect not constrained to valid IDs
12 Hardcoded 2 elements.ts FLOOR_ELEM_CYCLE should be derived
13 Hardcoded 1 elements.ts BASE_UNLOCKED_ELEMENTS should be derived
14 Hardcoded 2 attunements.ts XP formula grows very steep at high levels
15 Hardcoded 2 attunements.ts Misleading comment on XP scaling
16 Missing Entry 3 elements.ts ELEMENT_OPPOSITES incomplete for composite/exotic
17 Missing Entry elements.ts ELEMENT_ICON_NAMES — verified complete ✓
18 Missing Entry 4 guardians.ts No guardian defined for floor 70
19 Data Integrity crafting-recipes.ts All recipe equipment references valid ✓
20 Data Integrity spell-effects/ All spell ID references valid ✓
21 Missing Entry 2 defense-effects.ts DEFENSE_EFFECTS is empty
22 Naming 2 aoe-spells.ts Inconsistent AOE suffix naming
23 Naming 2 enchantments/ Inconsistent category prefix patterns
24 Naming 2 equipment/types.ts, spells.ts baseDamage vs dmg, baseCastSpeed vs castSpeed
25 Naming 1 attunements.ts, golems/types.ts primaryManaType vs baseManaType
26 Banned All files No lifesteal/healing found
27 Banned All files No banned mana types found
28 Banned 1 loot-drops.ts "Vitality Essence" name is suspicious
29 Type Ann. disciplines.ts Enum usage verified correct ✓
30 Type Ann. 2 attunements.ts getUnlockedAttunements return type hides undefined
31 Type Ann. 1 loot-drops.ts Minor semantic naming issue
32 Type Ann. 1 crafting-recipes.ts Unused import
33 Data Integ. 4 guardians.ts Floor 70 guardian missing (duplicate of 18)
34 Data Integ. 2 guardians.ts Inconsistent unlocksMana count per guardian
35 Data Integ. 1 enchantments/ Deep re-export chain
36 Data Integ. 1 equipment/types.ts Dual import paths for EquipmentSlot
37 Balance 2 raw-spells.ts, basic-elemental-spells.ts Tier 0 spells vastly outclassed
38 Balance 3 aoe-spells.ts, master-spells.ts AOE strictly better than single-target at tier 3
39 Balance legendary-spells.ts Legendary spell balance verified ✓
40 Balance 3 golems/utils.ts Negative maintenance cost at high skill
41 Balance 3 core.ts Negative study cost at high skill
42 Balance 2 core.ts No cap on study speed multiplier

TOP PRIORITY FIXES (Severity ≥ 3)

  1. Floor 70 guardian missing (Severity 4) — Add guardian definition at floor 70 in constants/guardians.ts
  2. Negative maintenance cost (Severity 3) — Add Math.max(0, ...) clamp in golems/utils.ts line 107
  3. Negative study cost (Severity 3) — Add Math.max(0, ...) clamp in constants/core.ts line 25
  4. rawCost/elemCost duplication (Severity 3) — Import from canonical source in golems/types.ts
  5. Missing discipline type exports (Severity 3) — Add to types/index.ts barrel
  6. Incomplete ELEMENT_OPPOSITES (Severity 3) — Add opposites for composite/exotic elements
  7. AOE vs single-target balance (Severity 3) — Rebalance tier 3 AOE or buff single-target
  8. Guardian effect type mismatch (Severity 3) — Add missing types to SpellEffect.type union
<longcat_arg_value>## Comprehensive Codebase Audit Report I read and analyzed all 50+ files across `data/`, `constants/`, and `types/` directories. Here are all findings organized by category. --- ### 1. DUPLICATE / CONFLICTING DEFINITIONS **[ISSUE 1]** `EquipmentSlot` defined in TWO places with potential for drift - `src/lib/game/types/equipmentSlot.ts` (line 3-10) — canonical type: `'mainHand' | 'offHand' | 'head' | 'body' | 'hands' | 'feet' | 'accessory1' | 'accessory2'` - `src/lib/game/data/equipment/types.ts` (line 3) — re-exports from `equipmentSlot.ts` via `export type { EquipmentSlot } from '../../types/equipmentSlot'` - **Severity: 2** — Currently consistent, but the re-export pattern means `data/equipment/types.ts` has a redundant re-export. The `EQUIPMENT_SLOTS` array in `data/equipment/types.ts` (line 6-7) manually lists the same values. If one changes without the other, they'll drift. - **Fix:** Remove the manual `EQUIPMENT_SLOTS` array and derive it from a shared source, or add a compile-time assertion. **[ISSUE 2]** `rawCost` and `elemCost` helper functions defined in THREE places - `src/lib/game/constants/elements.ts` (lines 7-14) — used by spell definitions - `src/lib/game/data/golems/types.ts` (lines 7-13) — duplicate implementation - `src/lib/game/types/spells.ts` — `SpellCost` interface defines the same shape - **Severity: 3** — The golem types file has its own copy of `rawCost`/`elemCost` that could diverge from the canonical versions in `constants/elements.ts`. - **Fix:** Import from `constants/elements.ts` in golem types instead of redefining. **[ISSUE 3]** `SPELL_EFFECTS` in `data/enchantments/spell-effects/index.ts` re-exports `EnchantmentEffectDef` from `../../enchantment-types` but the `index.ts` at `data/enchantments/index.ts` also re-exports the same type from `../enchantment-types`. The spell-effects sub-index re-exports it as a type-only export which is fine, but creates two import paths to the same type. - **Severity: 1** — Cosmetic, but adds confusion. **[ISSUE 4]** `ALL_CASTER` constant defined in `data/enchantments/spell-effects/types.ts` (line 7) is a local `['caster']` array. Meanwhile `data/enchantments/combat-effects.ts` defines its own `CASTER_AND_HANDS` locally. These are separate constants for overlapping concepts with no shared source. - **Severity: 2** — Minor inconsistency, could lead to drift. --- ### 2. MISSING EXPORTS / BROKEN RE-EXPORTS **[ISSUE 5]** `types/index.ts` does NOT export `DisciplinesAttunementType`, `PerkType`, `DisciplinePerk`, `DisciplineDefinition`, `DisciplineState`, `MAX_CONCURRENT_DISCIPLINES`, or `BASE_CONCURRENT_DISCIPLINES` from `types/disciplines.ts` - `src/lib/game/types/index.ts` (line 1) — only re-exports from `elements`, `attunements`, `spells`, `equipment`, `equipmentSlot`, and `game` - **Severity: 3** — Any code needing discipline types must import directly from `types/disciplines` rather than the barrel export, breaking the pattern used by all other type files. - **Fix:** Add `export type { DisciplinesAttunementType, PerkType, DisciplinePerk, DisciplineDefinition, DisciplineState } from './disciplines'` and `export { MAX_CONCURRENT_DISCIPLINES, BASE_CONCURRENT_DISCIPLINES } from './disciplines'` to `types/index.ts`. **[ISSUE 6]** `types/index.ts` does NOT export `GolemDef`, `GolemManaCost` from `data/golems/types.ts` — but these aren't in the `types/` directory, they're in `data/golems/types.ts`. The `types/index.ts` barrel doesn't cover data types at all. - **Severity: 2** — Inconsistent barrel pattern. Data types are not re-exported through any central barrel. **[ISSUE 7]** `constants/index.ts` does NOT export `ELEMENT_OPPOSITES`, `ELEMENT_ICON_NAMES`, or `BASE_UNLOCKED_ELEMENTS` from `elements.ts` — wait, actually it does re-export them (line 7). However, `FLOOR_ELEM_CYCLE` is NOT exported from the barrel. - `src/lib/game/constants/index.ts` (line 6) — exports `ELEMENTS` and `FLOOR_ELEM_CYCLE` is missing from the re-export list. - **Severity: 2** — Code needing `FLOOR_ELEM_CYCLE` must import directly from `constants/elements`. --- ### 3. TYPE MISMATCHES **[ISSUE 8]** `SpellEffect.type` in `types/spells.ts` (line 33) includes `'shield'` and `'buff'` as effect types, but these are never used in any spell definition across all spell module files. Conversely, spell definitions use effect types `'chain'`, `'aoe'`, `'armor_pierce'` which ARE used but the type definition does include `'armor_pierce'` — however `'chain'`, `'cast_speed'`, `'crit_chance'`, `'crit_damage'`, `'raw_damage'`, `'void_resist'`, `'all_damage'` used in guardian effects are NOT in the `SpellEffect.type` union. - `src/lib/game/types/spells.ts` (line 33): `type: 'burn' | 'freeze' | 'stun' | 'pierce' | 'multicast' | 'shield' | 'buff' | 'chain' | 'aoe' | 'armor_pierce'` - Guardian effects in `constants/guardians.ts` use: `'burn'`, `'armor_pierce'`, `'cast_speed'`, `'crit_chance'`, `'crit_damage'`, `'raw_damage'`, `'void_resist'`, `'all_damage'` - **Severity: 3** — Guardian effects use types `'cast_speed'`, `'crit_chance'`, `'crit_damage'`, `'raw_damage'`, `'void_resist'`, `'all_damage'` that are NOT in the `SpellEffect.type` union. If guardian effects are ever validated against this type, they'll fail. - **Fix:** Add missing types to the `SpellEffect.type` union. **[ISSUE 9]** `GuardianDef.effects` in `types/attunements.ts` (line 56) is typed as `{ type: string; value: number }[]` — using `string` instead of a union type. This is a weak type that bypasses type checking. - **Severity: 2** — Should use the same union as `SpellEffect.type` or a shared effect type. **[ISSUE 10]** `GolemDef` in `data/golems/types.ts` has `baseManaType: string` and `armorPierce: number` fields. The `GolemManaCost` interface in the same file duplicates the shape of `SpellCost` from `types/spells.ts` but with `element?: string` instead of the more specific typing. - `data/golems/types.ts` (lines 15-19): `GolemManaCost` = `{ type: 'raw' | 'element'; element?: string; amount: number }` - `types/spells.ts` (lines 5-9): `SpellCost` = `{ type: 'raw' | 'element'; element?: string; amount: number }` - **Severity: 2** — These are structurally identical but independently defined. Changes to one won't reflect in the other. **[ISSUE 11]** `AchievementDef.reward` in `types/game.ts` (lines 53-59) has `unlockEffect?: string` but the type doesn't constrain what values are valid. The `masterEnchanter` achievement uses `unlockEffect: 'efficiencyBoost'` but there's no enum or union for valid effect IDs. - **Severity: 1** — Minor, but could lead to typos going undetected. --- ### 4. HARDCODED VALUES THAT SHOULD BE DERIVED **[ISSUE 12]** `FLOOR_ELEM_CYCLE` in `constants/elements.ts` (line 76) is hardcoded as `["fire", "water", "air", "earth", "light", "dark", "death"]` — this should be derived from the `ELEMENTS` record's base category entries. - **Severity: 2** — If a base element is added/removed from `ELEMENTS`, the cycle won't update automatically. **[ISSUE 13]** `BASE_UNLOCKED_ELEMENTS` in `constants/elements.ts` (line 79) is hardcoded as `['transference']` — should be derived from `ELEMENTS` entries where `cat === 'utility'` or similar logic. - **Severity: 1** — Minor, but same derivation issue. **[ISSUE 14]** `MAX_ATTUNEMENT_LEVEL = 10` in `data/attunements.ts` (line 196) is hardcoded. The `getAttunementXPForLevel` function has a comment describing scaling up to level 5 but the max is 10 with no XP formula beyond level 5. - **Severity: 2** — The XP formula `Math.floor(1000 * Math.pow(2, level - 2) * (level >= 3 ? 1.25 : 1))` grows exponentially and will produce enormous numbers for levels 6-10. **[ISSUE 15]** `getAttunementXPForLevel` in `data/attunements.ts` (lines 178-186) has a broken formula. The comment says "each level requires 2x the previous, starting from 1000" but the code does `1000 * Math.pow(2, level - 2) * 1.25` for level 3+, which means: - Level 3: `1000 * 2^1 * 1.25 = 2500` ✓ (matches comment) - Level 4: `1000 * 2^2 * 1.25 = 5000` ✓ (matches comment) - Level 5: `1000 * 2^3 * 1.25 = 10000` ✓ (matches comment) - Level 6: `1000 * 2^4 * 1.25 = 20000` — but the comment says "etc." implying this continues - Level 10: `1000 * 2^8 * 1.25 = 320,000` — extremely steep - **Severity: 2** — The formula works but the comment is misleading about the scaling pattern. --- ### 5. MISSING ENTRIES IN ARRAYS/RECORDS **[ISSUE 16]** `ELEMENT_OPPOSITES` in `constants/elements.ts` (lines 68-72) is incomplete. It covers fire/water, air/earth, light/dark, and lightning/earth, but is missing opposites for: - `death` — no opposite defined - `metal` — no opposite defined - `sand` — no opposite defined - `crystal` — no opposite defined - `stellar` — no opposite defined - `void` — no opposite defined - `transference` — no opposite defined - **Severity: 3** — If the damage calculation uses opposites, composite/exotic elements have no defined opposite, which could cause runtime errors or undefined behavior. **[ISSUE 17]** `ELEMENT_ICON_NAMES` in `constants/elements.ts` (lines 74-79) is missing entries for composite and exotic elements. It has entries for all base + utility + composite + exotic elements (15 entries for 15 elements). Actually, checking: fire, water, air, earth, light, dark, death, transference, metal, sand, lightning, crystal, stellar, void, raw = 15 entries for 15 ELEMENTS entries. This is complete. ✓ **[ISSUE 18]** `GUARDIANS` in `constants/guardians.ts` has entries for floors 10, 20, 30, 40, 50, 60, 80, 90, 100 — but floor 70 is MISSING. The sequence jumps from 60 to 80. - **Severity: 4** — Floor 70 will have no guardian defined. If the game tries to access `GUARDIANS[70]`, it gets `undefined`, which will cause a runtime crash. - **Fix:** Add a guardian definition for floor 70. **[ISSUE 19]** `CRAFTING_RECIPES` in `data/crafting-recipes.ts` references equipment types like `'oakStaff'`, `'crystalWand'`, `'scholarRobe'`, `'arcanistStaff'`, `'battlestaff'`, `'fireCatalyst'`, `'runicShield'`, `'wizardHat'`, `'spellweaveGloves'`, `'travelerBoots'`, `'silverRing'`, `'silverAmulet'`. Cross-referencing with `EQUIPMENT_TYPES`: - `oakStaff` → ✓ exists in `casters.ts` - `crystalWand` → ✓ exists in `casters.ts` - `scholarRobe` → ✓ exists in `body.ts` - `arcanistStaff` → ✓ exists in `casters.ts` - `battlestaff` → ✓ exists in `casters.ts` - `fireCatalyst` → ✓ exists in `catalysts.ts` - `runicShield` → ✓ exists in `shields.ts` - `wizardHat` → ✓ exists in `head.ts` - `spellweaveGloves` → ✓ exists in `hands.ts` - `travelerBoots` → ✓ exists in `feet.ts` - `silverRing` → ✓ exists in `accessories.ts` - `silverAmulet` → ✓ exists in `accessories.ts` - All recipe equipment type references are valid. ✓ **[ISSUE 20]** Spell effect enchantments reference spell IDs via `spellId` in their `effect` field. Cross-referencing a sample: - `spell_manaBolt` → `spellId: 'manaBolt'` → ✓ exists in `RAW_SPELLS` - `spell_fireball` → `spellId: 'fireball'` → ✓ exists in `BASIC_ELEMENTAL_SPELLS` - `spell_stellarNova` → `spellId: 'stellarNova'` → ✓ exists in `LEGENDARY_SPELLS` - All spell effect enchantments reference valid spell IDs. ✓ **[ISSUE 21]** `DEFENSE_EFFECTS` in `data/enchantments/defense-effects.ts` is an EMPTY record `{}`. The file comment says "Currently empty - no defense effects defined." - **Severity: 2** — Having an entire category with zero effects suggests incomplete implementation. The `EnchantmentEffectCategory` type includes `'defense'` as a valid category, but no effects exist for it. --- ### 6. INCONSISTENT NAMING CONVENTIONS **[ISSUE 22]** Inconsistent spell naming: some use camelCase keys (`fireball`, `waterJet`, `stoneBullet`), but the AOE variants use a different pattern: `fireballAoe` (lowercase 'aoe') vs `earthquakeAoe`. The pattern is inconsistent — `fireballAoe` appends 'Aoe' while `earthquakeAoe` also appends 'aoe'. However, `meteorShower` and `blizzard` don't use the Aoe suffix despite being AOE spells. - `src/lib/game/constants/spells-modules/aoe-spells.ts` — `fireballAoe`, `frostNova`, `meteorShower`, `blizzard`, `earthquakeAoe`, `apocalypse` - **Severity: 2** — Inconsistent naming: some AOE spells have `Aoe` suffix, others don't. **[ISSUE 23]** Inconsistent enchantment effect ID naming: - Combat effects use `damage_5`, `damage_10`, `damage_pct_10`, `crit_5`, `attack_speed_10` (snake_case with numbers) - Mana effects use `mana_cap_50`, `mana_regen_1`, `click_mana_1` (snake_case with numbers) - Elemental effects use `sword_fire`, `sword_frost` (snake_case) - Special effects use `spell_echo_10`, `guardian_dmg_10` (snake_case with numbers) - Utility effects use `meditate_10`, `study_10`, `insight_5` (snake_case with numbers) - Spell effects use `spell_manaBolt`, `spell_fireball` (snake_case with camelCase spell IDs) - **Severity: 2** — The naming is internally consistent within categories but varies across categories. The spell effects prefix with `spell_` while others don't use category prefixes. **[ISSUE 24]** `EquipmentType` in `data/equipment/types.ts` uses `baseDamage` and `baseCastSpeed` for swords, but `SpellDef` in `types/spells.ts` uses `dmg` (not `baseDamage`) and `castSpeed` (not `baseCastSpeed`). Different naming for the same concepts. - **Severity: 2** — `baseDamage` vs `dmg`, `baseCastSpeed` vs `castSpeed` — inconsistent naming for equivalent concepts. **[ISSUE 25]** `AttunementDef` uses `primaryManaType` (optional, can be `undefined` for Invoker) while `GolemDef` uses `baseManaType` (required string). Different prefix for the same concept ("primary" vs "base"). - **Severity: 1** — Minor naming inconsistency. --- ### 7. BANNED CONTENT CHECK **[ISSUE 26]** ✅ **No lifesteal or healing found.** The `Drain` spell (death element, `Siphon vital energy`) and `Mana Rip` / `Essence Drain` (transference element, `Rip mana from the enemy`) deal damage — they don't heal the player. The `adrenaline_rush` special effect restores mana (not HP) on enemy defeat. No banned healing mechanics detected. **[ISSUE 27]** ✅ **No banned mana types found.** Scanned all files for `life`, `blood`, `wood`, `mental`, `force` as mana types. The `constants/elements.ts` file explicitly notes: "Life, Blood, Wood, Mental, and Force mana types have been removed." The `ManaType` union in `types/element.ts` contains only: `raw, fire, water, air, earth, light, dark, death, transference, metal, sand, lightning, crystal, stellar, void` — all valid. **[ISSUE 28]** ⚠️ `vitalityEssenceDrop` in `data/loot-drops.ts` (lines 103-109) — "Vitality Essence" could be confused with a life/healing mechanic, but it's typed as `'essence'` and grants elemental mana. The name is suspicious but the implementation doesn't violate banned content rules. - **Severity: 1** — Consider renaming to avoid confusion with banned life/healing concepts. --- ### 8. MISSING OR INCORRECT TYPE ANNOTATIONS **[ISSUE 29]** `DisciplineDefinition.attunement` field in `types/disciplines.ts` (line 37) is typed as `DisciplinesAttunementType` (the enum), but the discipline data files (`base.ts`, `enchanter.ts`, etc.) use string literals like `'base'`, `'enchanter'`, `'fabricator'`, `'invoker'`. The enum values match these strings, so this works, but the enum is a const enum-like pattern using `enum` with string values. This is actually correct since TypeScript string enums accept their defined values. ✓ **[ISSUE 30]** `getUnlockedAttunements` in `data/attunements.ts` (line 93) returns `AttunementDef[]` but uses `.filter(Boolean)` on the result of `.map()` which can include `undefined` values (when `ATTUNEMENTS_DEF[id]` doesn't exist). The return type claims it's `AttunementDef[]` but the `.filter(Boolean)` is needed precisely because `undefined` is possible. The type signature hides this. - **Severity: 2** — The function signature should acknowledge the potential for missing attunements, or the input should be constrained. **[ISSUE 31]** `getAvailableDrops` in `data/loot-drops.ts` (line 176) returns `LootDrop[]` but the filter uses `drop.minFloor > floor` which means drops with `minFloor === floor` are included. This is correct (≥), but the function name "available" could be clearer. - **Severity: 1** — Minor semantic issue. **[ISSUE 32]** `CraftingRecipe` in `data/crafting-recipes.ts` (line 7) imports `EquipmentSlot` from `./equipment/types` but doesn't use it in the interface. The `CraftingRecipe` interface has no `slot` field — the slot is determined by the resulting equipment type. - **Severity: 1** — Unused import (though this may be cleaned up by the compiler with `isolatedModules`). --- ### 9. DATA INTEGRITY ISSUES **[ISSUE 33]** `GUARDIANS` floor 70 is MISSING (see ISSUE 18 above). This is the most critical data integrity issue. **[ISSUE 34]** `GUARDIANS` floor 80 guardian (`Mors Ultima`) has `element: "death"` but `unlocksMana: ['death']` — it only unlocks its own element. Meanwhile floor 90 (`Primordialis`, void) unlocks `['void', 'stellar']` and floor 100 (`The Awakened One`, stellar) only unlocks `['stellar']`. The pattern is inconsistent — some guardians unlock 1 mana type, others unlock 2. - **Severity: 2** — Not a bug, but an inconsistency that could confuse players. **[ISSUE 35]** `enchantment-effects.ts` (line 1) re-exports everything from `enchantments/index`, which in turn imports from `spell-effects/index`. The `spell-effects/index.ts` imports from `types.ts` which re-exports `EnchantmentEffectDef` from `../../enchantment-types`. This creates a re-export chain: `enchantment-effects.ts` → `enchantments/index.ts` → `spell-effects/index.ts` → `spell-effects/types.ts` → `enchantment-types.ts`. The type `EnchantmentEffectDef` is available through multiple import paths. - **Severity: 1** — Not a bug, but the re-export chain is deep and could be simplified. **[ISSUE 36]** `data/equipment/types.ts` (line 3) re-exports `EquipmentSlot` from `../../types/equipmentSlot`, but `types/index.ts` also re-exportes it from `./equipmentSlot`. Code can import `EquipmentSlot` from either `data/equipment/types` or `types/index` — two canonical paths. - **Severity: 1** — Minor, but could cause confusion about which import path to use. --- ### 10. BALANCE ISSUES **[ISSUE 37]** `Mana Bolt` (tier 0) has `dmg: 5` at `cost: rawCost(3)` with `castSpeed: 3` → DPS = 15, mana cost per second = 9. `Ember Shot` (tier 1) has `dmg: 10` at `cost: elemCost("fire", 1)` with `castSpeed: 3` → DPS = 30, mana cost per second = 3. Ember Shot is 2x the DPS at 1/3 the mana cost of Mana Bolt. Tier 1 basic spells dramatically outclass tier 0 raw spells, which is expected progression, but the gap is very large. - **Severity: 2** — Expected progression, but the tier 0 spells may never be used. **[ISSUE 38]** `Apocalypse` (tier 3 AOE) has `dmg: 80` at `cost: elemCost("fire", 20)` with `castSpeed: 0.5` hitting 10 targets. Total DPS = 400, mana cost per second = 10. Compare to `Pyroclasm` (tier 3 single target): `dmg: 250` at `cost: elemCost("fire", 25)` with `castSpeed: 0.6` → DPS = 150, mana cost per second = 15. Apocalypse is both higher DPS AND more mana efficient while hitting 10 targets. This makes single-target tier 3 spells obsolete. - **Severity: 3** — AOE spells being strictly better than single-target spells at the same tier is a balance problem. **[ISSUE 39]** `Crystal Shatter` (tier 4, crystal) costs `elemCost("crystal", 10)` for `dmg: 400` at `castSpeed: 0.5` → DPS = 200, mana cost per second = 5. `Void Collapse` (tier 4, void) costs `elemCost("void", 12)` for `dmg: 450` at `castSpeed: 0.45` → DPS = 202.5, mana cost per second = 5.4. `Stellar Nova` (tier 4, stellar) costs `elemCost("stellar", 15)` for `dmg: 500` at `castSpeed: 0.4` → DPS = 200, mana cost per second = 6. All three legendary spells have nearly identical DPS/mana efficiency (~40 DPS per mana/sec), which is actually well-balanced. ✓ **[ISSUE 40]** `getGolemMaintenanceMultiplier` in `data/golems/utils.ts` (line 107) returns `1 - (skills.golemSiphon || 0) * 0.1`. At skill level 10, this returns `0` (free maintenance). At skill level 11+, it goes NEGATIVE (the game would pay you to maintain golems). There's no floor clamp. - **Severity: 3** — Missing `Math.max(0, ...)` clamp. High skill levels could cause negative maintenance costs, effectively generating mana. **[ISSUE 41]** `getStudyCostMultiplier` in `constants/core.ts` (line 25) returns `1 - (skills.focusedMind || 0) * 0.05`. At skill level 20+, this goes NEGATIVE (negative study costs). No floor clamp. - **Severity: 3** — Same issue. Should be `Math.max(0, ...)` or similar. **[ISSUE 42]** `getStudySpeedMultiplier` in `constants/core.ts` (line 21) returns `1 + (skills.quickLearner || 0) * 0.1`. No upper bound — at skill level 100, study speed is 11x. This may be intentional but could cause balance issues. - **Severity: 2** — No cap on study speed multiplier. --- ### SUMMARY TABLE | # | Category | Severity | File(s) | Description | |---|----------|----------|---------|-------------| | 1 | Duplicate | 2 | `equipmentSlot.ts`, `equipment/types.ts` | EquipmentSlot defined in two places | | 2 | Duplicate | 3 | `elements.ts`, `golems/types.ts` | `rawCost`/`elemCost` duplicated | | 3 | Duplicate | 1 | `enchantments/index.ts`, `spell-effects/index.ts` | Dual re-export paths for `EnchantmentEffectDef` | | 4 | Duplicate | 2 | `spell-effects/types.ts`, `combat-effects.ts` | Overlapping constants for equipment categories | | 5 | Missing Export | 3 | `types/index.ts` | Discipline types not in barrel export | | 6 | Missing Export | 2 | `types/index.ts` | Data types not covered by barrel | | 7 | Missing Export | 2 | `constants/index.ts` | `FLOOR_ELEM_CYCLE` not re-exported | | 8 | Type Mismatch | 3 | `spells.ts`, `guardians.ts` | Guardian effect types not in `SpellEffect.type` union | | 9 | Type Mismatch | 2 | `attunements.ts` | `GuardianDef.effects` uses weak `string` type | | 10 | Type Mismatch | 2 | `golems/types.ts`, `spells.ts` | `GolemManaCost` duplicates `SpellCost` shape | | 11 | Type Mismatch | 1 | `game.ts` | `unlockEffect` not constrained to valid IDs | | 12 | Hardcoded | 2 | `elements.ts` | `FLOOR_ELEM_CYCLE` should be derived | | 13 | Hardcoded | 1 | `elements.ts` | `BASE_UNLOCKED_ELEMENTS` should be derived | | 14 | Hardcoded | 2 | `attunements.ts` | XP formula grows very steep at high levels | | 15 | Hardcoded | 2 | `attunements.ts` | Misleading comment on XP scaling | | 16 | Missing Entry | 3 | `elements.ts` | `ELEMENT_OPPOSITES` incomplete for composite/exotic | | 17 | Missing Entry | — | `elements.ts` | `ELEMENT_ICON_NAMES` — verified complete ✓ | | 18 | Missing Entry | **4** | `guardians.ts` | **No guardian defined for floor 70** | | 19 | Data Integrity | — | `crafting-recipes.ts` | All recipe equipment references valid ✓ | | 20 | Data Integrity | — | spell-effects/ | All spell ID references valid ✓ | | 21 | Missing Entry | 2 | `defense-effects.ts` | `DEFENSE_EFFECTS` is empty | | 22 | Naming | 2 | `aoe-spells.ts` | Inconsistent AOE suffix naming | | 23 | Naming | 2 | enchantments/ | Inconsistent category prefix patterns | | 24 | Naming | 2 | `equipment/types.ts`, `spells.ts` | `baseDamage` vs `dmg`, `baseCastSpeed` vs `castSpeed` | | 25 | Naming | 1 | `attunements.ts`, `golems/types.ts` | `primaryManaType` vs `baseManaType` | | 26 | Banned | — | All files | ✅ No lifesteal/healing found | | 27 | Banned | — | All files | ✅ No banned mana types found | | 28 | Banned | 1 | `loot-drops.ts` | "Vitality Essence" name is suspicious | | 29 | Type Ann. | — | `disciplines.ts` | Enum usage verified correct ✓ | | 30 | Type Ann. | 2 | `attunements.ts` | `getUnlockedAttunements` return type hides undefined | | 31 | Type Ann. | 1 | `loot-drops.ts` | Minor semantic naming issue | | 32 | Type Ann. | 1 | `crafting-recipes.ts` | Unused import | | 33 | Data Integ. | **4** | `guardians.ts` | Floor 70 guardian missing (duplicate of 18) | | 34 | Data Integ. | 2 | `guardians.ts` | Inconsistent unlocksMana count per guardian | | 35 | Data Integ. | 1 | enchantments/ | Deep re-export chain | | 36 | Data Integ. | 1 | `equipment/types.ts` | Dual import paths for `EquipmentSlot` | | 37 | Balance | 2 | `raw-spells.ts`, `basic-elemental-spells.ts` | Tier 0 spells vastly outclassed | | 38 | Balance | 3 | `aoe-spells.ts`, `master-spells.ts` | AOE strictly better than single-target at tier 3 | | 39 | Balance | — | `legendary-spells.ts` | Legendary spell balance verified ✓ | | 40 | Balance | **3** | `golems/utils.ts` | **Negative maintenance cost at high skill** | | 41 | Balance | **3** | `core.ts` | **Negative study cost at high skill** | | 42 | Balance | 2 | `core.ts` | No cap on study speed multiplier | ### TOP PRIORITY FIXES (Severity ≥ 3) 1. **Floor 70 guardian missing** (Severity 4) — Add guardian definition at floor 70 in `constants/guardians.ts` 2. **Negative maintenance cost** (Severity 3) — Add `Math.max(0, ...)` clamp in `golems/utils.ts` line 107 3. **Negative study cost** (Severity 3) — Add `Math.max(0, ...)` clamp in `constants/core.ts` line 25 4. **`rawCost`/`elemCost` duplication** (Severity 3) — Import from canonical source in `golems/types.ts` 5. **Missing discipline type exports** (Severity 3) — Add to `types/index.ts` barrel 6. **Incomplete `ELEMENT_OPPOSITES`** (Severity 3) — Add opposites for composite/exotic elements 7. **AOE vs single-target balance** (Severity 3) — Rebalance tier 3 AOE or buff single-target 8. **Guardian effect type mismatch** (Severity 3) — Add missing types to `SpellEffect.type` union
Author
Owner

Audit complete. 42 issues found across 50+ files. Top priorities: missing floor 70 guardian (sev 4), negative maintenance/study costs at high skill levels (sev 3), duplicated rawCost/elemCost helpers (sev 3), missing discipline type barrel exports (sev 3), incomplete ELEMENT_OPPOSITES (sev 3), AOE vs single-target balance (sev 3), and guardian effect type mismatches (sev 3). No banned content (lifesteal, healing, life/blood/wood/mental/force mana) found. Full report in previous comment.

Audit complete. 42 issues found across 50+ files. Top priorities: missing floor 70 guardian (sev 4), negative maintenance/study costs at high skill levels (sev 3), duplicated rawCost/elemCost helpers (sev 3), missing discipline type barrel exports (sev 3), incomplete ELEMENT_OPPOSITES (sev 3), AOE vs single-target balance (sev 3), and guardian effect type mismatches (sev 3). No banned content (lifesteal, healing, life/blood/wood/mental/force mana) found. Full report in previous comment.
Author
Owner

Audit complete — 42 issues documented across 50+ files. Issue closed. Awaiting orchestrator direction on which fixes to implement.

Audit complete — 42 issues documented across 50+ files. Issue closed. Awaiting orchestrator direction on which fixes to implement.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Anexim/Mana-Loop#67