Skip to content
Merged
92 changes: 57 additions & 35 deletions AUDIT_REPORT.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,57 @@
# Neurotoxic Codebase Audit Report — Remaining Items

Original audit had ~95 findings. Items that have been processed (fixed,
merged, integrated, or verified non-issue) have been removed from this file.
Items that were intentionally skipped retain a one-line reason.

## 4. DEAD / UNREACHABLE CODE

### LOW

- **LOW** `src/schemas/crisis.json` — header says authoritative validation
is `validateCrisisEvent` in `eventValidator`; no code consumes the JSON
schema directly. **SKIPPED**: the JSON schema is documentary and
`validateCrisisEvent` now runs at startup via the §1 integration of
`validateGameEvent`. Leaving the schema as a developer reference is
consistent with that role; deletion would lose the human-readable doc.
- **LOW** `src/utils/imageGen.ts:4` — hardcoded API key (gitleaks-allowed).
**NOTE ONLY** per original audit.

## 5. MISSING INTEGRATION

### LOW

- **LOW** `GAME_PHASES.PRACTICE` is not in `SCENES_WITHOUT_HUD` in
`App.tsx:23-33`. **CONFIRMED INTENTIONAL**: practice mode mirrors a gig
session (HUD-on) — same treatment as `GAME_PHASES.GIG`, which is also
outside the set. No change needed.

## 6. NOTES

All HIGH-severity findings from the original audit have been processed.
The original §6 ("Headline metrics", "Top-priority fixes", "Highest-leverage
cleanup batches", "Audit confidence notes") is omitted from this trimmed
report — see the PR history on `claude/integrate-audit-report-MhGWV` for
the implementation timeline and per-commit rationale.
# Neurotoxic Code-Quality Audit Findings

Scope: `src/` primary audit, with targeted `tests/` ripgrep checks for orphan verification. Orphan claims below were verified with `rg`.

## DUPLICATES

No remaining findings in this section after the selected cleanup pass.

## ORPHANED / UNINTEGRATED CODE

| Severity | Location | Finding | Recommended action |
| -------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| HIGH | `src/ui/shared/BrutalistUI.tsx:964` | `CrisisModal` is fully built but has no `src/` consumer except the shared barrel export. Looks intended for crisis events but is never rendered. | INTEGRATE into crisis/event flow or DELETE. |
| MED | `src/ui/shared/BrutalistUI.tsx:743`, `:833`, `:892`, `:909`, `:1170`, `:1213` | `BrutalToggle`, `BrutalTabs`, `StatBlock`, `BrutalFader`, `BrutalSlot`, and `VoidLoader` are exported only through `src/ui/shared/index.tsx`; no real app consumers. | DELETE unused demo components or move to story/demo-only surface. |
| LOW | `src/ui/shared/BrutalistUI.tsx:304`, `:406`, `:485`, `:623`, `:678` | `MoneyIcon`, `SkullIcon`, `GearIcon`, `BiohazardIcon`, `CorporateSeal` are not used by app code. `MoneyIcon` has tests only. | DELETE unused icon exports or integrate intentionally. |
| LOW | `src/scenes/kabelsalat/kabelsalatConstants.ts:97` | `CONNECTOR_TYPES` is exported and covered by local AGENTS guidance, but has no code or test references. | DELETE export or INTEGRATE into Kabelsalat validation/UI. |
| LOW | `src/utils/minigameRegistry.ts:26` | `MinigameKey` exported type is unused; `MINIGAME_REGISTRY` itself is used. | DELETE unused type export. |
| LOW | `src/context/initialState.ts:76` | `DEFAULT_RIVAL_BAND_STATE` is exported but only used inside `initialState.ts`. | Make it file-local unless public API is intentional. |
| LOW | `src/context/usePersistence.ts:74` | `createPersistedState` is exported but only used inside `usePersistence.ts`. | Make it file-local or add explicit tests/imports if intended public API. |
| LOW | `src/utils/numberUtils.ts:22` | `formatNumber` has tests but no `src/` consumers; UI still formats many numbers inline. | INTEGRATE into numeric UI or remove public export. |

## INCONSISTENCIES

| Severity | Location | Finding | Recommended action |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| HIGH | `src/hooks/rhythmGame/useRhythmGameAudio.ts:14`, `src/hooks/rhythmGame/useRhythmGameScoring.ts:18`, `src/components/pregig/SetlistBlock.tsx:4`, `src/hooks/usePreGigLogic.ts:20` | Files outside `src/utils/audio/` import audio submodules directly, violating `src/utils/audio/AGENTS.md`; imports should go through `audioEngine.ts`. | FIX imports and re-export missing rhythm helpers from `audioEngine.ts` if needed. |
| MED | `src/context/actionCreators.ts:650`, `src/domain/questLifecycle.ts:208` | `createAdvanceQuestAction` passes raw `amount`; quest lifecycle only caps upper bound. Negative, `NaN`, or infinite values can corrupt progress. | FIX by sanitizing finite non-negative amount in action creator and clamping final progress. |
| MED | `src/context/actionCreators.ts:566`, `src/context/reducers/rivalReducer.ts:56` | `createUpdateRivalBandAction` claims sanitized payload, but `powerLevel` is copied raw and reducer blindly merges. | FIX finite/bounds validation in creator and reducer. |
| MED | `src/ui/GigModifierButton.tsx:41`, `src/ui/bandhq/StatsTab.tsx:24`, `src/ui/BloodBankModal.tsx:68`, `src/ui/DarkWebLeakModal.tsx:41`, `src/ui/bandhq/ShopItem.tsx:117` | Money is rendered with raw `€` strings despite project rule to use locale-aware `formatCurrency`. | FIX with `formatCurrency(value, i18n.language, ...)` and bare locale placeholders where text is translated. |
| MED | `src/ui/bandhq/DetailedStatsTab.tsx:104`, `src/ui/bandhq/CatalogTab.tsx:8`, `src/ui/bandhq/LeaderboardTab.tsx:281`, `src/ui/overworld/OverworldHUD.tsx:186`, `src/ui/MerchPressModal.tsx:121`, `src/ui/MerchPressModal.tsx:166`, `src/utils/effectFormatter.ts:44` | Additional money/currency displays also hardcode `€` or append a raw suffix instead of using the shared formatter/i18n path. | FIX with locale-aware currency formatting or translated display labels. |
| MED | `src/context/gameReducer.ts:172`, `src/context/gameReducer.ts:212`, `src/context/reducers/bandReducer.ts:541` | `@ts-expect-error` and `assertNever(action as never)` weaken the project’s strict TypeScript/exhaustiveness conventions. | FIX reducer-map/switch typing so no suppression/cast-to-never is needed. |
| LOW | `src/ui/SupplyStopModal.tsx:86` | Hardcoded Tailwind arbitrary RGBA shadow color violates design-token color rule. | FIX with CSS var/token-based shadow. |
| LOW | `src/components/stage/stageRenderUtils.ts:9`, `src/hooks/rhythmGame/useRhythmGameState.ts:180` | Pixi color fallbacks duplicate hardcoded token hex values and lane colors pass explicit hardcoded fallbacks. | FIX by centralizing/generated token fallback mapping or relying on token helper defaults. |

## DEAD / UNREACHABLE CODE

| Severity | Location | Finding | Recommended action |
| -------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| MED | `src/data/events/transport.ts:98`, `src/data/events/transport.ts:146` | Story flags `VAN_DAMAGED` and `RENTAL_VAN` are written but never read anywhere in `src/` or `tests/`. Their consequences are inert. | INTEGRATE flag checks into van/rental systems or DELETE the flags. |
| MED | `src/ui/shared/BrutalistUI.tsx:833` | `BrutalTabs` renders placeholder “Loading module” panels but has no app consumer, so this UI path is unreachable. | DELETE or replace with real integrated tabs. |
| LOW | `src/scenes/kabelsalat/kabelsalatConstants.ts:97` | `CONNECTOR_TYPES` is derived but not consumed by code, so changes to it cannot affect gameplay. | DELETE or wire into validation/render logic. |

## MISSING INTEGRATION

| Severity | Location | Finding | Recommended action |
| -------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| HIGH | `src/data/events/transport.ts:98`, `src/data/events/transport.ts:146` | Transport event outcomes appear to set durable consequences, but `VAN_DAMAGED` and `RENTAL_VAN` are never used by travel, economy, van condition, or UI. | INTEGRATE into van condition/rental logic, or remove misleading flags. |
| HIGH | `src/ui/shared/BrutalistUI.tsx:964` | `CrisisModal` looks like a completed feature surface, but crisis events currently flow through generic event handling instead. | INTEGRATE with crisis thresholds/events or delete as unused UI. |
| MED | `src/utils/numberUtils.ts:22` | Tested number formatter is not wired into stats, shop, leaderboard, or HUD numeric display paths. | INTEGRATE into numeric display components if locale formatting is desired. |
| MED | `src/utils/audio/audioEngine.ts:54`, `src/utils/audio/audioEngine.ts:55`, `src/hooks/rhythmGame/useRhythmGameAudio.ts:14` | Audio hub re-exports `songUtils` and `timingUtils`, but not `rhythmGameAudioUtils`; rhythm-game audio helpers are still imported directly. | INTEGRATE rhythm-game audio exports into `audioEngine.ts` and update callers. |

## Checks With No Findings

- EN/DE locale key comparison found no missing keys between `public/locales/en` and `public/locales/de`.
- Literal `t('namespace:key')` lookups scanned from `src/` had no missing locale keys.
- `ActionTypes` were present in both reducer mapping and action creator coverage.
- No direct Tone.js timing reads outside the audio layer were found for gameplay timing.
33 changes: 10 additions & 23 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,29 +76,16 @@ const SceneLoadingFallback = () => {
* @returns {JSX.Element} Das gerenderte React-Element, das die aktive Szene und alle globalen Overlays/Hilfekomponenten enthält.
*/
function GameContent() {
const {
currentScene,
activeEvent,
settings,
minigameType,
band,
player,
gameMap,
social,
gigModifiers,
lastGigStats
} = useGameSelector(state => ({
currentScene: state.currentScene,
activeEvent: state.activeEvent,
settings: state.settings,
minigameType: state.minigame?.type,
band: state.band,
player: state.player,
gameMap: state.gameMap,
social: state.social,
gigModifiers: state.gigModifiers,
lastGigStats: state.lastGigStats
}))
const currentScene = useGameSelector(state => state.currentScene)
const activeEvent = useGameSelector(state => state.activeEvent)
const settings = useGameSelector(state => state.settings)
const minigameType = useGameSelector(state => state.minigame?.type)
const band = useGameSelector(state => state.band)
const player = useGameSelector(state => state.player)
const gameMap = useGameSelector(state => state.gameMap)
const social = useGameSelector(state => state.social)
const gigModifiers = useGameSelector(state => state.gigModifiers)
const lastGigStats = useGameSelector(state => state.lastGigStats)

const { resolveEvent } = useGameActions()

Expand Down
18 changes: 5 additions & 13 deletions src/components/MinigameSceneFrame.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
/*
* (#1) Actual Updates: Added strict isRequired to MinigameSceneFrame logic props and added this block.
* (#2) Next Steps: Extract specific minigame UIs from this component.

*/

import { useEffect, useLayoutEffect, useRef } from 'react'
import { motion } from 'framer-motion'
import { useGameState } from '../context/GameState'
import { useGameActions, useGameSelector } from '../context/GameState'
import { PixiStage } from './PixiStage'
import { ActionButton } from '../ui/shared'
import type { MinigameSceneFrameProps } from '../types/components'
Expand All @@ -29,14 +23,13 @@ export const MinigameSceneFrame = <TState,>({
completionButtonText = 'CONTINUE',
children
}: MinigameSceneFrameProps<TState>) => {
const settings = useGameSelector(state => state.settings)
const {
settings,
completeTravelMinigame,
completeRoadieMinigame,
completeKabelsalatMinigame,
completeAmpCalibration,
endGig
} = useGameState()
completeAmpCalibration
} = useGameActions()
const continueButtonRef = useRef<HTMLButtonElement | null>(null)
const previousFocusRef = useRef<HTMLElement | null>(null)
const logicRef = useRef(logic)
Expand Down Expand Up @@ -128,8 +121,7 @@ export const MinigameSceneFrame = <TState,>({
completeTravelMinigame,
completeRoadieMinigame,
completeKabelsalatMinigame,
completeAmpCalibration,
endGig
completeAmpCalibration
])

return (
Expand Down
5 changes: 0 additions & 5 deletions src/components/PixiStageController.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/*
* (#1) Actual Updates: Replaced insecure Math.random() with state.rng() for screen shake effect based on PR feedback.
* (#2) Next Steps: Ensure no regressions in Pixi rendering or tests.
* (#3) Found Errors + Solutions: Replaced getSafeRandom with state.rng() for deterministic performance in the main loop.
*/
import type { Container } from 'pixi.js'
import { ToxicFilterManager } from './stage/ToxicFilterManager'
import { BaseStageController } from './stage/BaseStageController'
Expand Down
11 changes: 0 additions & 11 deletions src/components/SceneRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
/*
* REVIEW
* #1 Actual updates:
* Extracted SceneRouter component and its lazy-loaded scene imports from App.tsx into this dedicated file.
* Added strict TypeScript prop typing using enums for currentScene and minigameType.
* #2 Next steps and ideas to develop further:
* Consider dynamic loading of scene configurations from a data file, or creating nested routers for complex minigames.
* #3 Found errors + solutions:
* None during extraction.
*/

import { lazy } from 'react'
import { MainMenu } from '../scenes/MainMenu.tsx'
import { GAME_PHASES, MINIGAME_TYPES } from '../context/gameConstants'
Expand Down
4 changes: 0 additions & 4 deletions src/components/clinic/ClinicHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
/*
* (#1) Actual Updates: Extracted ClinicHeader into a separate component.

*/
import { useTranslation } from 'react-i18next'
import { formatCurrency } from '../../utils/numberUtils'
import type { ClinicHeaderProps } from '../../types/components'
Expand Down
4 changes: 0 additions & 4 deletions src/components/clinic/ClinicMemberCard.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import { formatCurrency } from '../../utils/numberUtils'
/*
* (#1) Actual Updates: Extracted ClinicMemberCard into a separate component.

*/
import React from 'react'
import { motion } from 'framer-motion'
import { useTranslation } from 'react-i18next'
Expand Down
11 changes: 0 additions & 11 deletions src/components/postGig/DealCard.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
/**
* (#1) Actual Updates:
* - Extracted subcomponents (`DealImage`, `DealInfo`, `DealActions`) from the main `DealCard` component.
* - Reduced complexity and overall length of the `DealCard` render function.
*
* (#2) Next Steps:
* - Check if other complex cards in the DealsPhase or SocialPhase can benefit from similar granular extraction.
*
* (#3) Found Errors + Solutions:
* - Complexity was getting high due to multiple conditionals and inline rendering logic. Solution: Used `memo()` wrapper on new granular display components and moved their relevant prop types to themselves.
*/
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import { ActionButton } from '../../ui/shared'
Expand Down
4 changes: 2 additions & 2 deletions src/components/postGig/DealsPhase.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import { useGameState } from '../../context/GameState'
import { useGameSelector } from '../../context/GameState'
import { useDealNegotiation } from '../../hooks/useDealNegotiation'
import { DealCard } from './DealCard'
import { NegotiationModal } from './NegotiationModal'
import type { DealsPhaseProps } from '../../types/components'

const DealsPhaseComponent = ({ offers, onAccept, onSkip }: DealsPhaseProps) => {
const { t } = useTranslation()
const { social } = useGameState()
const social = useGameSelector(state => state.social)

const {
negotiatedDeals,
Expand Down
5 changes: 0 additions & 5 deletions src/components/pregig/GigModifiersBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
/**
* (#1) Actual Updates: Extracted GigModifiersBlock from PreGig scene
* (#2) Next Steps: Extract remaining logic

*/
import { motion } from 'framer-motion'
import { useTranslation } from 'react-i18next'
import GigModifierButton from '../../ui/GigModifierButton'
Expand Down
4 changes: 0 additions & 4 deletions src/components/pregig/PreGigHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
/*
* (#1) Actual Updates: Extracted PreGigHeader into a separate component.

*/
import React from 'react'
import { motion } from 'framer-motion'
import { formatCurrency } from '../../utils/numberUtils'
Expand Down
Loading
Loading