Skip to content

CallDebugger Refactoring Implementation Plan

Date: 2026-02-04 Design Document: 2026-02-04-calldebugger-refactor-design.md Branch: debugger-dashboard

Overview

This plan breaks the CallDebugger refactoring into small, testable increments. Tasks are ordered to minimize breaking changes: backend changes first, then utilities, then frontend components, and finally cleanup/deletion of obsolete files.

Phase 1: Backend Changes

Task 1.1: Add new call lookup endpoint

Goal: Create GET /api/debug/db/calls/:id endpoint that supports both retell and arini call IDs.

Files to modify: - /src/worker/modules/debug/router.ts - Add new route - /src/worker/modules/debug/queries/calls.ts - Add/modify query function

Implementation: 1. Add new route GET /db/calls/:id in router.ts 2. Create getCallById query function that: - Accepts either retell_call_id (call_xxx) or arini_call_id (UUID) - Returns: { call_id, arini_call_id, date, env, agent_id, agent_name, direction, created_at } - Uses single SQL query with WHERE retell_call_id = :id OR arini_call_id = :id

Testing: - Test with retell call ID (call_xxx) - Test with arini call ID (UUID) - Test with non-existent ID (should return 404)

Dependencies: None


Task 1.2: Update auth middleware

Goal: Remove the lookup endpoint from public paths since it's being replaced.

Files to modify: - /src/worker/middleware/auth.ts - Remove /api/debug/db/calls/lookup from PUBLIC_PATHS

Testing: - Verify /api/debug/db/calls/lookup now requires auth - Verify /api/debug/db/calls/:id requires auth

Dependencies: Task 1.1


Task 1.3: Remove old lookup endpoint

Goal: Remove the /api/debug/db/calls/lookup endpoint.

Files to modify: - /src/worker/modules/debug/router.ts - Remove the lookup route - /src/worker/modules/debug/queries/calls.ts - Remove lookupCall function if no longer used

Testing: - Verify endpoint returns 404

Dependencies: Task 1.2, Task 3.1 (frontend must be updated first to use new endpoint)


Phase 2: Utility Files

Task 2.1: Create deduplicatedFetch utility

Goal: Create a simple request deduplication utility to replace callStore's dedupe logic.

Files to create: - /src/ui/modules/debug/utils/deduplicatedFetch.ts

Implementation:

const pending = new Map<string, Promise<unknown>>()

export async function deduplicatedFetch<T>(
  key: string,
  fetcher: () => Promise<T>
): Promise<T> {
  const existing = pending.get(key)
  if (existing) return existing as Promise<T>

  const promise = fetcher().finally(() => pending.delete(key))
  pending.set(key, promise)
  return promise
}

Testing: - Unit test: concurrent calls with same key return same promise - Unit test: key is removed after promise resolves/rejects

Dependencies: None


Task 2.2: Create dashboardState utility

Goal: Create sessionStorage helpers for preserving dashboard state when navigating to/from debugger.

Files to create: - /src/ui/modules/debug/utils/dashboardState.ts

Implementation:

const STORAGE_KEY = 'debug:dashboard'

type DashboardState = {
  date: string
  env: Environment
  orgId: string | null
  locationId: string | null
  agentId: string | null
}

export function saveDashboardState(state: DashboardState) { ... }
export function loadDashboardState(): DashboardState | null { ... }
export function clearDashboardState() { ... }

Testing: - Unit test: save/load/clear cycle works correctly - Unit test: returns null when no state saved

Dependencies: None


Phase 3: Frontend Components

Task 3.1: Create useCall hook

Goal: Create a hook to fetch single call metadata by ID using the new endpoint.

Files to create: - /src/ui/modules/debug/hooks/useCall.ts

Files to modify: - /src/ui/modules/debug/services/CallDebuggerService.ts - Add getCall(id) function

Implementation:

// In CallDebuggerService.ts
export async function getCall(id: string, signal?: AbortSignal): Promise<CallMetadata | null> {
  const response = await fetch(`/api/debug/db/calls/${encodeURIComponent(id)}`, { signal })
  if (response.status === 404) return null
  if (!response.ok) throw new Error(`Failed to fetch call: ${response.statusText}`)
  return response.json()
}

// In useCall.ts
export function useCall(callId: string | null) {
  const [data, setData] = useState<CallMetadata | null>(null)
  const [error, setError] = useState<Error | null>(null)
  const [isLoading, setIsLoading] = useState(false)

  useEffect(() => {
    if (!callId) return
    // Use deduplicatedFetch to fetch call metadata
    // ...
  }, [callId])

  return { data, error, isLoading, notFound: error?.message === 'Call not found' }
}

Testing: - Hook returns loading state initially - Hook returns data on success - Hook returns notFound error on 404

Dependencies: Task 1.1, Task 2.1


Task 3.2: Create CallNotFound component

Goal: Create error state component with option to check dev environment.

Files to create: - /src/ui/modules/debug/components/CallNotFound.tsx

Implementation: Per design document - shows error message with callId, button to check dev environment, and back to dashboard link.

Testing: - Component renders with callId - "Check Dev Environment" button triggers dev lookup - "Back to Dashboard" navigates to /debug

Dependencies: Task 3.1 (needs useCall or similar for dev check)


Task 3.3: Create BottomSheet component

Goal: Create mobile bottom sheet component for turn details.

Files to create: - /src/ui/components/BottomSheet.tsx (shared component)

Implementation: Per design document: - Swipe down to dismiss - Drag handle at top - Max height 80vh - Backdrop overlay - Animation for open/close

Testing: - Component renders when open=true - Swipe gesture dismisses sheet - Backdrop click dismisses sheet - Content scrolls within sheet

Dependencies: None


Task 3.4: Update TurnExplorerView for mobile bottom sheet

Goal: Use BottomSheet for turn details on mobile instead of side panel.

Files to modify: - /src/ui/modules/debug/components/TurnExplorerView.tsx

Implementation:

const isMobile = useIsMobile()

{selectedTurn && (
  isMobile ? (
    <BottomSheet open={!!selectedTurn} onClose={() => setSelectedTurn(null)}>
      <TurnDetailsPanel turn={selectedTurn} />
    </BottomSheet>
  ) : (
    <div className="w-1/2 border-l">
      <TurnDetailsPanel turn={selectedTurn} />
    </div>
  )
)}

Testing: - On mobile viewport: details appear in bottom sheet - On desktop viewport: details appear in side panel - Sheet dismisses on swipe/backdrop click

Dependencies: Task 3.3


Task 3.5: Update routes

Goal: Update route structure to /debug/call/:callId (simplified from /debug/call/:env/:date/:callId).

Files to modify: - /src/ui/modules/debug/index.tsx - Update route definitions

Old routes:

{ path: "call/:env/:date/:callId", element: <CallDebugger /> }
{ path: "call", element: <CallDebugger /> }
{ path: ":callId", element: <CallLookupRedirect /> }

New routes:

{ path: "call/:callId", element: <CallDebugger /> }

Testing: - /debug/call/call_xxx loads CallDebugger with correct callId - /debug/call/uuid loads CallDebugger with correct callId - Old routes redirect appropriately (or show 404)

Dependencies: Task 3.1


Task 3.6: Refactor CallDebugger component

Goal: Remove sidebar, simplify to route-based, use useCall hook.

Files to modify: - /src/ui/modules/debug/pages/CallDebugger.tsx

Changes: 1. Remove all sidebar-related code: - Remove CallIdSidebar import and usage - Remove sidebarCollapsed state - Remove handleToggleSidebar function - Remove sidebar toggle button - Remove mobile overlay mask - Remove useResizable hook

  1. Remove legacy URL param handling:
  2. Remove params.date, params.env usage
  3. Simplify to just useParams<{ callId: string }>()

  4. Use new useCall hook:

  5. Replace existing call resolution logic
  6. Extract env, date from useCall result

  7. Add CallNotFound error handling:

  8. Show CallNotFound when useCall returns notFound

  9. Remove props (no longer embedded in CallsLandingPage):

  10. Remove CallDebuggerProps type
  11. Component only reads from URL params

Testing: - Direct navigation to /debug/call/:callId works - Call metadata loads and displays correctly - View switching (turns/raw/waterfall/data) works - Mobile header collapse still works - Filter controls still work

Dependencies: Task 3.1, Task 3.2, Task 3.5


Task 3.7: Update CallsLandingPage to navigate to new route

Goal: Update navigation from dashboard to use new route structure.

Files to modify: - /src/ui/modules/debug/pages/CallsLandingPage.tsx

Changes: 1. Update handleSelectCall to navigate to /debug/call/:callId 2. Save dashboard state before navigation (using Task 2.2 utility) 3. Remove embedded CallDebugger rendering (no longer inline) 4. Remove isDebugging state and related logic

Testing: - Clicking call in sidebar navigates to /debug/call/:callId - Dashboard state is saved before navigation - Back button returns to dashboard with state restored

Dependencies: Task 2.2, Task 3.5, Task 3.6


Task 3.8: Restore dashboard state on mount

Goal: Restore dashboard state from sessionStorage when returning from debugger.

Files to modify: - /src/ui/modules/debug/pages/CallsLandingPage.tsx

Changes: 1. On mount, check for saved dashboard state 2. If found, restore date/env/orgId/locationId/agentId 3. Clear saved state after restore (one-time)

Testing: - Navigate to call, then back - state is restored - Refresh dashboard - no crash (handles missing state) - State is cleared after restoration

Dependencies: Task 2.2, Task 3.7


Phase 4: Cleanup

Task 4.1: Update DebugLayout

Goal: Remove CallDebuggerProvider since context is no longer needed.

Files to modify: - /src/ui/modules/debug/DebugLayout.tsx

Changes:

// Before
export function DebugLayout() {
  return (
    <CallDebuggerProvider>
      <Outlet />
    </CallDebuggerProvider>
  )
}

// After
export function DebugLayout() {
  return <Outlet />
}

Dependencies: Task 3.6, Task 3.7 (ensure nothing uses context)


Task 4.2: Delete CallDebuggerContext

Goal: Remove the context file since it's no longer used.

Files to delete: - /src/ui/modules/debug/contexts/CallDebuggerContext.tsx

Testing: - Build succeeds without errors - No runtime errors related to missing context

Dependencies: Task 4.1


Task 4.3: Delete callStore

Goal: Remove the data store file since hooks now manage their own state.

Files to delete: - /src/ui/modules/debug/data/callStore.ts

Testing: - Build succeeds without errors

Dependencies: Task 4.1, Task 4.2


Task 4.4: Delete CallIdSidebar

Goal: Remove the old sidebar component and styles.

Files to delete: - /src/ui/modules/debug/components/CallIdSidebar.tsx - /src/ui/modules/debug/components/CallIdSidebar.css

Testing: - Build succeeds without errors

Dependencies: Task 3.6


Task 4.5: Delete useDBCalls hook

Goal: Remove the hook since it was only used by CallIdSidebar.

Files to delete: - /src/ui/modules/debug/hooks/useDBCalls.ts

Testing: - Build succeeds without errors

Dependencies: Task 4.4


Task 4.6: Delete CallLookupRedirect

Goal: Remove the lookup redirect page and styles since lookup is handled by the new endpoint.

Files to delete: - /src/ui/modules/debug/pages/CallLookupRedirect.tsx - /src/ui/modules/debug/pages/CallLookupRedirect.css

Testing: - Build succeeds without errors

Dependencies: Task 3.5


Task 4.7: Clean up CallDebuggerService

Goal: Remove legacy functions no longer needed.

Files to modify: - /src/ui/modules/debug/services/CallDebuggerService.ts

Functions to remove: - lookupCall (replaced by getCall)

Testing: - Build succeeds without errors - Remaining functions work correctly

Dependencies: Task 1.3, Task 3.1


Task 4.8: Clean up types

Goal: Update types to reflect new API response shape.

Files to modify: - /src/ui/modules/debug/types/debugger.ts

Changes: - Update CallMetadata type to include env field - Remove unused types if any

Dependencies: Task 3.1


Task Dependency Graph

Phase 1 (Backend):
  1.1 ──────────────┬──> 1.2 ──> 1.3
Phase 2 (Utilities):│
  2.1 ──────────────┼──> 3.1
  2.2 ──────────────┼──> 3.7, 3.8
Phase 3 (Frontend): │
  3.1 ──────────────┼──> 3.2, 3.5, 3.6
  3.3 ──────────────┼──> 3.4
  3.5 ──────────────┼──> 3.6, 3.7, 4.6
  3.6 ──────────────┼──> 3.7, 4.1, 4.4
  3.7 ──────────────┼──> 3.8, 4.1
Phase 4 (Cleanup):  │
  4.1 ──────────────┼──> 4.2, 4.3
  4.2 ──────────────┤
  4.3 ──────────────┤
  4.4 ──────────────┼──> 4.5
  4.5 ──────────────┤
  4.6 ──────────────┤
  1.3, 3.1 ─────────┼──> 4.7
  3.1 ──────────────┼──> 4.8
  1. Phase 2 first (utilities) - No dependencies, can be done immediately
  2. 2.1: deduplicatedFetch
  3. 2.2: dashboardState

  4. Phase 1 (backend) - Independent of frontend

  5. 1.1: New endpoint
  6. 1.2: Update auth (after 1.1)

  7. Phase 3 (frontend) - Core refactoring

  8. 3.1: useCall hook (needs 1.1, 2.1)
  9. 3.3: BottomSheet (no dependencies)
  10. 3.2: CallNotFound (needs 3.1)
  11. 3.4: TurnExplorerView update (needs 3.3)
  12. 3.5: Routes update (needs 3.1)
  13. 3.6: CallDebugger refactor (needs 3.1, 3.2, 3.5)
  14. 3.7: CallsLandingPage update (needs 2.2, 3.5, 3.6)
  15. 3.8: Dashboard state restore (needs 2.2, 3.7)

  16. Phase 4 (cleanup) - After frontend is working

  17. 4.1: DebugLayout (needs 3.6, 3.7)
  18. 4.2: Delete context (needs 4.1)
  19. 4.3: Delete callStore (needs 4.1, 4.2)
  20. 4.4: Delete CallIdSidebar (needs 3.6)
  21. 4.5: Delete useDBCalls (needs 4.4)
  22. 4.6: Delete CallLookupRedirect (needs 3.5)
  23. 1.3: Remove old lookup endpoint (needs 3.1)
  24. 4.7: Clean up service (needs 1.3, 3.1)
  25. 4.8: Clean up types (needs 3.1)

Summary of File Changes

Create (7 files)

File Task
src/ui/modules/debug/utils/deduplicatedFetch.ts 2.1
src/ui/modules/debug/utils/dashboardState.ts 2.2
src/ui/modules/debug/hooks/useCall.ts 3.1
src/ui/modules/debug/components/CallNotFound.tsx 3.2
src/ui/components/BottomSheet.tsx 3.3

Modify (11 files)

File Tasks
src/worker/modules/debug/router.ts 1.1, 1.3
src/worker/modules/debug/queries/calls.ts 1.1, 1.3
src/worker/middleware/auth.ts 1.2
src/ui/modules/debug/services/CallDebuggerService.ts 3.1, 4.7
src/ui/modules/debug/components/TurnExplorerView.tsx 3.4
src/ui/modules/debug/index.tsx 3.5
src/ui/modules/debug/pages/CallDebugger.tsx 3.6
src/ui/modules/debug/pages/CallsLandingPage.tsx 3.7, 3.8
src/ui/modules/debug/DebugLayout.tsx 4.1
src/ui/modules/debug/types/debugger.ts 4.8

Delete (7 files)

File Task
src/ui/modules/debug/contexts/CallDebuggerContext.tsx 4.2
src/ui/modules/debug/data/callStore.ts 4.3
src/ui/modules/debug/components/CallIdSidebar.tsx 4.4
src/ui/modules/debug/components/CallIdSidebar.css 4.4
src/ui/modules/debug/hooks/useDBCalls.ts 4.5
src/ui/modules/debug/pages/CallLookupRedirect.tsx 4.6
src/ui/modules/debug/pages/CallLookupRedirect.css 4.6

Risk Mitigation

  1. Breaking changes: Backend changes (Phase 1) are done first and the old endpoint is kept until frontend is updated.

  2. Rollback: Each task is a discrete commit. If issues arise, individual changes can be reverted.

  3. Testing: Each task includes specific testing criteria. Run full test suite after each phase.

  4. Mobile UX: BottomSheet (3.3) is built and tested independently before integrating with TurnExplorerView.

  5. State preservation: dashboardState utility (2.2) is tested in isolation before integrating with navigation flow.