🎯 Objective
Decompose the monolithic 228-line useTaskMonitor hook into focused, testable sub-hooks following the Single Responsibility Principle.
📁 Files to Modify
| Action |
File Path |
Description |
| Modify |
frontend/src/hooks/useTaskMonitor.ts |
Refactor into composition of sub-hooks |
📁 Files to Create
| Action |
File Path |
Description |
| Create |
frontend/src/hooks/useTaskWebSocket.ts |
WebSocket connection management |
| Create |
frontend/src/hooks/useNodeState.ts |
Node state machine and transitions |
| Create |
frontend/src/hooks/useTaskPayments.ts |
Payment status tracking |
| Create |
frontend/src/hooks/useTaskOutputs.ts |
Output aggregation from completed nodes |
📁 Files to Create (Tests)
| Action |
File Path |
Description |
| Create |
frontend/src/hooks/useTaskWebSocket.test.ts |
WebSocket hook tests |
| Create |
frontend/src/hooks/useNodeState.test.ts |
Node state hook tests |
| Create |
frontend/src/hooks/useTaskPayments.test.ts |
Payments hook tests |
📁 Files to Modify (Tests)
| Action |
File Path |
Description |
| Modify |
frontend/src/pages/TaskDetailPage.tsx |
Update import if API changes |
🔍 Current Monolithic Structure (useTaskMonitor.ts — 228 lines)
function useTaskMonitor(taskId: string) {
// 1. WebSocket connection + reconnect (lines 1-60)
// 2. Node state management (lines 61-120)
// 3. Payment tracking (lines 121-160)
// 4. Output aggregation (lines 161-190)
// 5. Mock data fallback (lines 191-210)
// 6. Error handling (lines 211-228)
return { task, nodes, payments, outputs, isConnected, error };
}
✅ Expected Decomposition
1. frontend/src/hooks/useTaskWebSocket.ts
interface UseTaskWebSocketOptions {
taskId: string;
onMessage: (event: DAGEvent) => void;
onConnect?: () => void;
onDisconnect?: () => void;
}
function useTaskWebSocket(options: UseTaskWebSocketOptions) {
// WebSocket connection
// Exponential backoff reconnection
// Cursor-based replay
// Heartbeat management
return { isConnected, reconnect, disconnect };
}
2. frontend/src/hooks/useNodeState.ts
function useNodeState(taskId: string) {
// Node state machine (pending → running → completed/failed)
// State transitions from DAGEvent
// Current node status map
return { nodes, getNodeStatus, getCompletedNodes };
}
3. frontend/src/hooks/useTaskPayments.ts
function useTaskPayments(taskId: string) {
// Payment status tracking per node
// Payment history
// Total cost calculation
return { payments, totalCost, getNodePayment };
}
4. frontend/src/hooks/useTaskOutputs.ts
function useTaskOutputs(taskId: string) {
// Aggregate outputs from completed nodes
// Final merged result
// Output formatting
return { outputs, finalResult, getNodeOutput };
}
5. Refactored useTaskMonitor.ts (Composition)
function useTaskMonitor(taskId: string) {
const [lastEvent, setLastEvent] = useState<DAGEvent | null>(null);
const { isConnected } = useTaskWebSocket({
taskId,
onMessage: setLastEvent,
});
const { nodes } = useNodeState(taskId);
const { payments } = useTaskPayments(taskId);
const { outputs, finalResult } = useTaskOutputs(taskId);
return { task, nodes, payments, outputs, finalResult, isConnected, error };
}
📁 Reference Files
| File Path |
Purpose |
frontend/src/hooks/useTaskMonitor.ts |
Current 228-line monolith to decompose |
frontend/src/pages/TaskDetailPage.tsx |
Consumer of useTaskMonitor |
frontend/src/types/api.ts |
DAGEvent type definition |
frontend/src/services/api.ts |
API client |
📋 Migration Strategy
- Create the 4 new sub-hooks
- Write tests for each sub-hook
- Refactor
useTaskMonitor to compose sub-hooks
- Verify
TaskDetailPage still works
- Remove mock data fallback from production code
- Remove old inline logic from
useTaskMonitor
✅ Acceptance Criteria
🎯 Objective
Decompose the monolithic 228-line
useTaskMonitorhook into focused, testable sub-hooks following the Single Responsibility Principle.📁 Files to Modify
frontend/src/hooks/useTaskMonitor.ts📁 Files to Create
frontend/src/hooks/useTaskWebSocket.tsfrontend/src/hooks/useNodeState.tsfrontend/src/hooks/useTaskPayments.tsfrontend/src/hooks/useTaskOutputs.ts📁 Files to Create (Tests)
frontend/src/hooks/useTaskWebSocket.test.tsfrontend/src/hooks/useNodeState.test.tsfrontend/src/hooks/useTaskPayments.test.ts📁 Files to Modify (Tests)
frontend/src/pages/TaskDetailPage.tsx🔍 Current Monolithic Structure (useTaskMonitor.ts — 228 lines)
✅ Expected Decomposition
1.
frontend/src/hooks/useTaskWebSocket.ts2.
frontend/src/hooks/useNodeState.ts3.
frontend/src/hooks/useTaskPayments.ts4.
frontend/src/hooks/useTaskOutputs.ts5. Refactored
useTaskMonitor.ts(Composition)📁 Reference Files
frontend/src/hooks/useTaskMonitor.tsfrontend/src/pages/TaskDetailPage.tsxfrontend/src/types/api.tsfrontend/src/services/api.ts📋 Migration Strategy
useTaskMonitorto compose sub-hooksTaskDetailPagestill worksuseTaskMonitor✅ Acceptance Criteria
useTaskWebSockethook with connection managementuseNodeStatehook with state machine logicuseTaskPaymentshook for payment trackinguseTaskOutputshook for output aggregationuseTaskMonitorto compose all 4 sub-hooksmock-task-e2e-123TaskDetailPagerenders correctlynpm test— all tests passnpm run build— no TypeScript errors