WikifitaGitHub live67e8de5
outro · camdom/camdom-code-quality

CAMDOM — Code Quality, Patterns & Technical Debt

Code review: quality metrics, naming conventions, structural patterns, error handling, code duplication, type safety, technical debt, commit analysis.

Baixar raw

CAMDOM — Code Quality, Patterns & Technical Debt

Project Overview

MetricValue
Total Commits429
Date Range2024-06-06 to 2025-11-16 (17 months)
Contributors6 (alefita: 142, GitHub Action: 116, alefita-p4b: 90, Mikael: 46, Alef Oliveira: 22, Victor: 8)
Total Source Lines~8,967 (TypeScript + Kotlin + Swift)
TypeScript Strict ModeEnabled
Test Coverage0% — No test files exist

1. Code Quality Metrics

File Sizes (Largest to Smallest)

RankFileLinesCategory
1modules/ble-manager/src/BleManager.ts1,877BLE Logic (God Object)
2app/index.tsx918Main UI Screen
3modules/ble-manager/android/.../BleManager.kt753Android Native BLE
4modules/ble-manager/android/.../BlePeripheralManager.kt452Android Peripheral
5modules/ble-manager/ios/BlePeripheralManager.swift273iOS Peripheral
6utils/i18n.ts247Internationalization
7app/onboarding.tsx246Onboarding UI
8components/Menu/MenuList.tsx237Menu Component
9utils/Animations.ts164Animation Engine
10components/Menu/MenuTitle.tsx159Menu Title

Critical Finding: BleManager.ts at 1,877 lines is 25.9% of all TypeScript source code. This is a textbook god object antipattern.

TypeScript any Usage

TypeCountLocation
Explicit any type annotations5BleManager.ts:873,886,892 (event handler)
as any type casts3BleManager.ts (event casting)
Total any violations8All in BleManager.ts

Lines with any:

  • Line 873: emit(name: EventNames, ...args: any[])
  • Line 885: (event as any).value.data
  • Line 892: (event as any).value

TODO/FIXME/HACK Markers

FileLineMarkerContent
BleManager.kt319TODOtimeout timer: if callback not called - disconnect, wait 120ms, close
BleManager.kt329TODObonding state
BleManager.kt344TODOrandom error 133 - close and try reconnect
BleManager.kt391WARNcharacteristic not found $CHAR_FOR_INDICATE_UUID

Analysis: All TODOs are in the Android native layer, indicating incomplete BLE error recovery. The error 133 handler (line 344) is a known Android BLE issue that remains unimplemented.

Commented-Out Code Blocks

FileLinesCountFeature
BleManager.ts587-5893Debug logging for device scan
BleManager.ts1106-11138RSSI proximity sensor activation
BleManager.ts1198-12058RSSI proximity sensor activation (duplicate)
BleManager.ts1268-12758RSSI proximity sensor activation (duplicate)
BleManager.ts1463-14686Proximity notification UI
BleManager.ts1483-14919RSSI notification handler
BleManager.ts1799-18046Client RSSI packet sending
BleManager.ts1814-184936Server RSSI processing + alarm firing
BleManager.ts1868-18703RSSI timeout configuration
Options.tsx117-14428Sign out + Delete account buttons
Total~115

Critical Finding: 109 lines of commented-out code in BleManager.ts alone. The RSSI proximity feature is completely disabled but remains as zombie code. The Options.tsx file has commented-out authentication features (sign out, delete account).

Magic Numbers vs Named Constants

Magic NumberLocationContextShould Be
125, 124, 126, 249, 251BleManager.ts:499-507Advertising value rangesNamed constants for iOS/Android ranges
53BleManager.ts:909MTU request sizeconst MTU_SIZE = 53
8BleManager.ts:438Session ID substring lengthconst SESSION_ID_LENGTH = 8
7500BleManager.ts:768Scanning timeout msconst SCAN_TIMEOUT_MS = 7500
500BleManager.ts:1001Server disconnection poll intervalconst DISCONNECT_POLL_MS = 500
3500BleManager.ts:1114RSSI reader delayconst RSSI_DELAY_MS = 3500
300BleManager.ts:981Connection sleep delayconst CONNECTION_DELAY_MS = 300
15BleManager.ts:1803,1848RSSI tick rateconst RSSI_TICK_MS = 15
5BleManager.ts:1859RSSI history buffer sizeconst RSSI_BUFFER_SIZE = 5
10BleManager.ts:601iOS scan retry limitconst IOS_SCAN_RETRY_LIMIT = 10
1800BleManager.ts:1514Ping pong timeoutconst PING_PONG_TIMEOUT_MS = 1800
600BleManager.ts:1523Disconnect acknowledgment delayconst DISCONNECT_ACK_MS = 600
1500BleManager.ts:1296Client disconnect timeoutconst CLIENT_DISCONNECT_TIMEOUT_MS = 1500

Result: 13+ magic numbers identified. Zero named constants for timing/range values.


2. Naming Conventions Analysis

TypeScript Conventions

PatternStatusExamples
camelCase functionsPASSstartScanning(), stopAdvertising(), handlePacketFromServer()
PascalCase componentsPASSLateralMenu, Options, MenuList, MenuTitle
PascalCase interfacesPASSUISharedValues, EventBase, EventWithPayload
SCREAMING_SNAKE constantsPARTIALSERVICE_UUID, CHAR_FOR_READ_UUID (BLE UUIDs only)
Boolean naming with is/hasPASSisScanning, isConnecting, isServer, hasConnectionError

Issues Found:

  • AvailablePositions type at line 53: "center" | "center" | "center" — duplicate union members (likely a bug or dead code)
  • randomIntFromInterval (line 491) — should be getRandomInt or randomInt
  • PacketToSoundsMapper and SoundsToPacketMapper — inconsistent naming (one uses To, other uses ToPacket)

Cross-Language Consistency (TS → Kotlin → Swift)

ConceptTypeScriptKotlinSwift
BLE ManagerBleManagerBleManagerBlePeripheralManager
Service UUIDSERVICE_UUIDSERVICE_UUIDSERVICE_UUID
Characteristic UUIDCHAR_FOR_READ_UUIDCHAR_FOR_READ_UUIDCHAR_FOR_INDICATE_UUID
Broadcast packetbroadcastPacket()broadcastPacket()broadcastPacket()
Send to clientsendPacketToClient()sendPacketToClient()

Finding: Naming is largely consistent across languages. The iOS implementation uses BlePeripheralManager instead of BleManager, which is a minor inconsistency but semantically accurate (iOS focuses on peripheral role).


3. Structural Patterns

The "God Object" Problem — BleManager.ts

BleManager.ts at 1,877 lines contains:

ResponsibilityLinesMethods
BLE state management~20015+ private state variables
Sound effects~80bindSoundEffects(), playSound()
Event listeners~230bindListeners()
Scanning logic~180startScanning(), stopScanning(), configureScanTimeout()
Connection management~150registerClientCallbacks(), disconnectFromServer()
Packet handling (server)~220handlePacketFromClient()
Packet handling (client)~200handlePacketFromServer()
UI interactions~180onDisconnectPressStart(), onDisconnectPressFinish(), onStopAlarmPressStart()
RSSI proximity (disabled)~80configureClientRSSITimeout(), configureServerRSSITimeout()
Storage initialization~10Constructor

Recommendation: Split into:

  1. BleConnectionManager — scanning, connecting, disconnecting
  2. BlePacketHandler — packet parsing and routing
  3. BleSoundManager — audio effects
  4. BleRssiMonitor — proximity detection (currently disabled)
  5. BleStateManager — SharedValue management

Module Separation

modules/
├── ble-manager/           # BLE native module (Expo Module)
│   ├── src/               # TypeScript layer
│   │   ├── BleManager.ts  # 1,877 lines — GOD OBJECT
│   │   ├── Events.types.ts
│   │   ├── Utils.ts
│   │   └── BleManagerModule.ts
│   ├── android/           # Kotlin implementation
│   │   └── src/main/java/p4b/modules/blemanager/
│   │       ├── BleManager.kt (753 lines)
│   │       ├── BlePeripheralManager.kt (452 lines)
│   │       └── BleManagerModule.kt (56 lines)
│   └── ios/               # Swift implementation
│       ├── BlePeripheralManager.swift (273 lines)
│       └── BleManagerModule.swift (42 lines)
├── app/                   # UI screens
│   ├── index.tsx (918 lines)
│   └── onboarding.tsx (246 lines)
├── components/            # Reusable UI
│   ├── LateralMenu/       # 6 files
│   └── Menu/              # 7 files
├── hooks/                 # Custom React hooks
│   └── useLateralMenu.tsx
└── utils/                 # Utilities
    ├── Animations.ts      # Generator-based animation engine
    ├── i18n.ts
    └── requestPermissions.ts

Issues:

  • No clear separation between UI and business logic
  • app/index.tsx at 918 lines is also oversized
  • No service layer or repository pattern
  • BLE protocol logic mixed with UI state management

File Naming Conventions

PatternStatusExamples
PascalCase componentsPASSLateralMenu.tsx, Options.tsx, MenuList.tsx
camelCase hooksPASSuseLateralMenu.tsx
camelCase utilitiesPASSAnimations.ts, i18n.ts, requestPermissions.ts
PascalCase classesPASSBleManager.ts
Consistent extensionsPASS.tsx for components, .ts for logic

4. Error Handling Audit

Try/Catch Blocks in BleManager.ts

LineContextHandling Quality
194-206Audio.setAudioModeAsync()POOR — Sets isAudioModeSet = false but no user feedback
511-521startAdvertising()POOR — Silently retries permissions, no error propagation
649-673device.connect()GOOD — Logs error, updates UI state, sets hasConnectionError
794-797connectedDevice.cancelConnection()POOR — Empty catch: //ignore
824-865sendPacketToServer()GOOD — Logs error, disconnects, updates UI
928-955monitorCharacteristicForService()POOR — Updates UI but no error logging

Total catch blocks: 6 Empty/ignoring catch blocks: 1 (cancelConnection) Silent failures: 2 (setAudioModeAsync, startAdvertising)

Missing Error Handling

LocationRisk
bindSoundEffects() (line 177)No catch — if audio files fail to load, isSoundsLoaded stays false
registerClientCallbacks() (line 902)No catch on discoverAllServicesAndCharacteristics()
handlePacketFromServer() (line 1327)No validation of packet format before parsing
handlePacketFromClient() (line 1529)No validation of packet.data format
playSound() (line 217)No catch on setVolume() or replayAsync()

Error Propagation Pattern

Native Module Error
    ↓
TypeScript Event Emitter
    ↓
BleManager Event Handler
    ↓
SharedValue UI State Update
    ↓
React Component Re-render
    ↓
Snackbar.show() (optional)

Issues:

  • Errors are swallowed in 33% of catch blocks
  • No centralized error handling or error boundary
  • User-facing errors only shown via Snackbar (no retry mechanism)
  • No error codes or structured error objects

5. Code Duplication

Lateral Menu Components (6 files)

FileLinesAnimation Pattern
LateralMenu.tsx73makeAnimation + stagger + timing
MenuIcon.tsx79makeAnimation + stagger + timing
CamdomLogo.tsxSame pattern
UnlockYourPleasure.tsxSame pattern
BuyAComdomLateralText.tsx79Same pattern
BillyBoyVerticalLogo.tsxSame pattern

Duplication: Each component independently defines:

const animation = makeAnimation(
  function* ({ isOpened, display, transition }) {
    "worklet";
    let to = 1;
    while (true) {
      yield* waitUntil(isOpened, !to);
      display.value = isOpened.value;
      yield* stagger(duration / 2, timing(transition, { to, duration }));
      to = to === 1 ? 0 : 1;
    }
  },
  { isOpened: false, display: false, transition: 0 }
);

Recommendation: Extract to useMenuAnimation() hook with configurable duration and stagger delay.

State Reset Code in BleManager.ts

The following block appears 5 times with minor variations (lines 317-333, 366-372, 529-534, 690-698, 1033-1044):

this.allowedClientsToDisconnect = [];
this.askedDisconnectionDevices = [];
this.askedToStopAlarmClients = [];
this.rssiUpdatesFromClients = new Map<string, number[]>();
this.connectedDevice = undefined; // or this.connectedClients = []
this.lastDisconnectedDevice = undefined;
this.hasAnyoneRequestedToFireWhenDisconnect = false;
this.hasAnyoneHigherSensibility = undefined;

Recommendation: Extract to resetConnectionState() method.

Disconnection Handler Duplication

onDisconnectPressFinish() (lines 1058-1146) and onDisconnectLongPress() (lines 1148-1225) share ~80% identical code for server-side disconnection handling. The only difference is the long press version sets hasConnectionError = false.


6. Type Safety

TypeScript Strict Mode Status

Enabled in tsconfig.json:

{
  "compilerOptions": {
    "strict": true
  }
}

Interface vs Type Usage

PatternCountLocation
interface declarations4Events.types.ts:1,3,7,14
type declarations5BleManager.ts:44,53,102
Mixed usageYESInconsistent

Issues:

  • UISharedValues uses interface (line 92)
  • AppConnectionState uses type (line 44)
  • No clear rule for when to use interface vs type

SharedValue Type Safety

SharedValueTypeLineSafety
isPeripheralServerSharedValue<boolean>93SAFE
hasConnectionErrorSharedValue<boolean>94SAFE
alarmButtonPositionSharedValue<AvailablePositions>95SAFE
ballPositionSharedValue<AvailablePositions>96SAFE
ballRadiusSharedValue<number>97SAFE
ballTextSharedValue<string[]>98SAFE
appStateSharedValue<AppConnectionState>99SAFE

Finding: SharedValue types are well-defined. However, AvailablePositions type (line 53) is suspicious:

export type AvailablePositions = "center" | "center" | "center";

This appears to be a bug — likely should be "left" | "center" | "right".

Critical Type Bug

AvailablePositions at line 53:

export type AvailablePositions = "center" | "center" | "center";

This type only allows "center" as a value. If the ball/alarm positions are supposed to move left/right, this type is broken. The animation code references "left" and "right" positions, but the type system would reject them.


7. Technical Debt Inventory

Deprecated APIs

APILocationReplacement
expo-modules-core EventEmitterBleManager.ts:3Modern Expo Module API
react-native-ble-plx BlePlxManagerBleManager.ts:21-26Consider Expo BLE native module
btoa() / atob()BleManager.ts:831,937Use TextEncoder/TextDecoder

Workarounds & Hacks

IssueLocationDescription
Platform-specific scan delayBleManager.ts:716Platform.OS === "ios" ? 400 : 0 — iOS needs 400ms delay before scanning
MTU negotiationBleManager.ts:909Hardcoded requestMTU(53) — should negotiate
Session ID truncationBleManager.ts:438uuid.v4().substring(0, 8) — arbitrary truncation
iOS scan retry logicBleManager.ts:601-608Complex retry with scanCountRetriesIos counter
Volume manager workaroundBleManager.ts:235-238Forces volume to 1 before playing sounds

Commented-Out Features

FeatureLinesStatus
RSSI Proximity Detection1791-1871DISABLED — 80 lines of dead code
Proximity Notifications1106-1113, 1198-1205, 1268-1275DISABLED — Snackbar UI for proximity
RSSI Packet Sending1799-1804DISABLED — Client RSSI reporting
Sign Out / Delete AccountOptions.tsx:117-144DISABLED — Authentication features

Zombie Variables (Allocated but Unused)

VariableLinePurpose
isAmIClientAllowedToDisconnectEdgeCase116Complex disconnection state
hasAnyoneHigherSensibility142RSSI sensitivity (disabled feature)
distanceAlarmFired149Distance-based alarm (disabled feature)
timeoutRssiReader150RSSI reader timeout (disabled feature)
pingPongTimeout151Ping pong protocol timeout

Missing Tests

Zero test files found. No .test.ts, .test.tsx, .spec.ts, or .spec.tsx files exist in the entire codebase.

Risk Areas Without Tests:

  • BLE packet parsing (custom protocol)
  • State machine transitions
  • Connection/disconnection logic
  • RSSI threshold calculations (disabled but still in code)
  • Sound effect loading and playback

8. Commit Quality Analysis

Message Quality

CategoryCountPercentage
Semantic (feat/fix/chore)~12028%
Descriptive~28065%
WIP/Temporary~297%

WIP Commits Found

HashMessage
8db976fwip
b19cdc4final bitch - wip
8712884wip: almost alive babe

Time-of-Day Patterns

Based on commit timestamps, development peaks at:

  • Evening (17:00-19:00 BRT) — Most commits
  • Night (21:00-02:00 BRT) — Significant activity
  • Early Morning (03:00-06:00 BRT) — Some commits present

Fix-Heavy Periods

PeriodPattern
Initial setup (Jun 2024)Build configuration fixes
Nov 2025Final push with rapid fixes

9. Architectural Strengths

Generator-Based Animation Engine (utils/Animations.ts)

Lines: 164

This is an elegant, production-quality animation system:

export function* timing(value: SharedValue<number>, rawConfig?: TimingConfig) {
  "worklet";
  const from = value.value;
  const { to, easing, duration } = { ...defaultTimingConfig, ...rawConfig };
  const start: number = yield;
  const end = start + duration;
  for (let current = start; current < end; ) {
    const progress = easing((current - start) / duration);
    const val = interpolate(progress, [0, 1], [from, to]);
    value.value = val;
    current += yield* timeSincePreviousFrame();
  }
  value.value = to;
}

Strengths:

  • Leverages JavaScript generators for imperative-style async animations
  • Composable via parallel() and stagger()
  • Runs on UI thread via "worklet" directive
  • Clean API: yield* timing(value, { to: 1, duration: 300 })

Custom BLE Protocol

The packet protocol is purpose-built and efficient:

c:0:125        → Connection (role:client, advertisingValue:125)
s:a|session1   → Session answer (sessionId: session1)
d:r|s          → Disconnection request start
a:f|1|u        → Alarm fire (action:1, source:user)
sound:1|3      → Sound play (type:1, sound:alarm)
n:rssi|1|45|null → RSSI update (enabled:1, value:45, sense:null)

Strengths:

  • Minimal overhead (ASCII-encoded, ~20 bytes typical)
  • Bidirectional (server/client roles)
  • Self-describing (colon-delimited type:content)
  • Supports migration (device role switching)

Skia Rendering

  • GPU-accelerated rendering via @shopify/react-native-skia
  • No bridge overhead for animations
  • Custom font rendering with Paragraph API
  • Shader-based effects (blur, image shaders)

Privacy by Design

  • No network calls except BLE (peer-to-peer)
  • No analytics or telemetry
  • No data collection — all state is local (MMKV)
  • No server dependency — fully decentralized

10. Architectural Weaknesses

BleManager as Monolith

1,877 lines handling:

  • BLE scanning
  • Connection management
  • Packet parsing/routing
  • Sound effects
  • UI state synchronization
  • RSSI monitoring (disabled)
  • Storage initialization

Impact:

  • Changes to one feature risk breaking others
  • Difficult to test in isolation
  • Merge conflicts likely in team development
  • Cognitive load for new developers

Shared State Sprawl

11 SharedValues managed by BleManager:

interface UISharedValues {
  isPeripheralServer: SharedValue<boolean>;
  hasConnectionError: SharedValue<boolean>;
  alarmButtonPosition: SharedValue<AvailablePositions>;
  ballPosition: SharedValue<AvailablePositions>;
  ballRadius: SharedValue<number>;
  ballText: SharedValue<string[]>;
  appState: SharedValue<AppConnectionState>;
}

Plus 15 private state variables:

private isServer?: boolean;
private isScanning = false;
private isConnecting = false;
private connectedDevice?: Device;
private connectedClients: string[] = [];
// ... 10 more

Total state count: 22 state variables in a single class.

Missing Abstractions

PatternStatusImpact
Repository PatternMISSINGDirect MMKV access scattered across codebase
Service LayerMISSINGBusiness logic mixed with UI
State MachineMISSINGConnection states managed via if/else chains
Dependency InjectionMISSINGSingleton pattern (const manager = new BleManager(...))
Event BusPARTIALNative EventEmitter used, but not centralized

No Tests

Risk Assessment:

ComponentRiskReason
BLE ProtocolCRITICALCustom packet format, no validation
State TransitionsHIGHComplex conditional logic
Connection LogicHIGHRace conditions possible
Sound EffectsMEDIUMPlatform-specific behavior
UI AnimationsLOWVisual feedback only

No CI/CD Quality Gates

No evidence of:

  • Linting (ESLint)
  • Type checking (tsc --noEmit)
  • Test execution
  • Code coverage
  • Bundle size analysis
  • Automated code review

11. Cross-Language Naming Inconsistencies

ConceptTypeScriptKotlinSwiftIssue
Main classBleManagerBleManagerBlePeripheralManageriOS naming differs
Read characteristicCHAR_FOR_READ_UUIDCHAR_FOR_READ_UUIDiOS doesn't use read char
Indicate characteristicCHAR_FOR_INDICATE_UUIDCHAR_FOR_INDICATE_UUIDCHAR_FOR_INDICATE_UUIDConsistent
Broadcast methodbroadcastPacket()broadcastPacket()broadcastPacket()Consistent
Send to clientsendPacketToClient()sendPacketToClient()iOS handles differently

Finding: Naming is largely consistent. The iOS BlePeripheralManager name is semantically accurate (iOS BLE peripherals have different APIs than Android).


12. Protocol-Level Issues

Packet Validation

No validation on incoming packets:

// BleManager.ts:1327
private async handlePacketFromServer(packet: string) {
  const packetType = packet.split(":")[0];  // Could crash if no ":"
  const packetContent = packet.split(":")[1]; // Could be undefined
  // ...
}

Risks:

  • Malformed packets could crash the app
  • No bounds checking on array access
  • No packet authentication

Race Conditions

Multiple async operations without proper locking:

// BleManager.ts:539-544
async startScanning() {
  this.isScanning = true;  // Set immediately
  // ... async operations ...
  // isScanning could be false before async completes
}

Memory Leaks

Potential leaks in event listeners:

// BleManager.ts:253
const subscriptions = [];
subscriptions.push(
  AppState.addEventListener("change", (state) => {
    // Closure captures `this`
  })
);

If bindListeners() is called multiple times, old subscriptions aren't cleaned up.


13. Recommendations

Immediate (Quick Wins)

  1. Extract magic numbers to named constants
  2. Fix AvailablePositions type — should be "left" | "center" | "right"
  3. Remove commented-out code — 115 lines of dead code
  4. Add error logging to silent catch blocks
  5. Fix cancelConnection empty catch — at minimum log the error

Short-Term (1-2 weeks)

  1. Split BleManager.ts into 4-5 focused modules
  2. Extract resetConnectionState() method
  3. Add packet validation in handlePacketFromServer and handlePacketFromClient
  4. Add ESLint with strict rules
  5. Add TypeScript CI check (tsc --noEmit)

Medium-Term (1 month)

  1. Implement state machine for connection states
  2. Add unit tests for BLE protocol parsing
  3. Extract animation hooks from lateral menu components
  4. Remove disabled RSSI feature or implement properly
  5. Add integration tests for BLE connection flow

Long-Term (3 months)

  1. Add repository pattern for storage access
  2. Implement dependency injection for testability
  3. Add E2E tests with Detox
  4. Set up CI/CD with quality gates
  5. Consider architecture overhaul — MVVM or Clean Architecture

14. Summary Scorecard

CategoryScoreGrade
Code Organization4/10D
Type Safety6/10C
Error Handling4/10D
Test Coverage0/10F
Documentation2/10F
Naming Conventions7/10B-
Code Duplication3/10D
Technical Debt3/10D
Architecture4/10D
Overall3.6/10D-

Key Strengths

  • Generator-based animation engine (elegant, composable)
  • Custom BLE protocol (efficient, purpose-built)
  • Privacy by design (no data collection)
  • TypeScript strict mode enabled

Key Weaknesses

  • God object antipattern (BleManager: 1,877 lines)
  • Zero test coverage
  • 115 lines of commented-out code
  • 13+ magic numbers
  • 22 state variables in single class
  • No CI/CD quality gates

Appendix: File Line Counts

7281 total
1877 ./modules/ble-manager/src/BleManager.ts
 918 ./app/index.tsx
 753 ./modules/ble-manager/android/.../BleManager.kt
 452 ./modules/ble-manager/android/.../BlePeripheralManager.kt
 273 ./modules/ble-manager/ios/BlePeripheralManager.swift
 247 ./utils/i18n.ts
 246 ./app/onboarding.tsx
 237 ./components/Menu/MenuList.tsx
 164 ./utils/Animations.ts
 159 ./components/Menu/MenuTitle.tsx
 147 ./components/Menu/MenuContent/Pages/Options/Options.tsx
 128 ./components/NoisyBlurredBackground.tsx
 101 ./components/Menu/MenuContent/MenuContent.tsx
  99 ./hooks/useLateralMenu.tsx
  81 ./components/Menu/Menu.tsx
  79 ./components/LateralMenu/MenuIcon.tsx
  79 ./components/LateralMenu/BuyAComdomLateralText.tsx
  77 ./components/LateralMenu/styles.ts
  73 ./components/LateralMenu/LateralMenu.tsx

This analysis reflects the codebase as of November 2025 (429 commits). The codebase demonstrates strong domain expertise in BLE protocol design and animation systems, but suffers from typical startup engineering tradeoffs: rapid iteration at the expense of maintainability. The generator-based animation engine is production-quality; the BLE manager needs architectural refactoring.