Problem
The TasksManager class (723 lines) is difficult to maintain due to:
- Inline PubSub handler in constructor (~50 mixed concerns)
- Deeply nested conditionals in
hasCompleted() (8 levels)
createTask() at 320+ lines doing too much
- Loose type casting hiding potential errors
These hurt debugging and onboarding when things break at 3am.
Plan
1. Extract inline PubSub handler (~1-2 hrs)
Current: Lines 56-108 in constructor - JSON parsing, validation, state updates all together.
Refactor:
- Create
TaskProgressHandler class
- Move JSON parsing and validation there
- Keep state updates in TasksManager
Why: Easier to debug when PubSub fails, testable in isolation.
2. Break up `hasCompleted()` method (~2-3 hrs)
Current: 562-650 - 8 levels of nested conditionals, impossible to trace which process stops when.
Refactor:
// From:
if (!fetch.stoppedAt && fetch.status === TaskStatus.Done) { ... }
if (!signature.stoppedAt && ...) { ... }
// To:
private checkAndStopFetch(task: MiningTask): Promise<void>
private checkAndStopSignature(task: MiningTask): Promise<void>
private checkAndStopExtract(task: MiningTask): Promise<void>
private checkAndStopClean(task: MiningTask): Promise<void>
Why: When a fetch task doesn't stop as expected, you can debug one method instead of unwinding 8 levels.
3. Split `createTask()` method (~2-3 hrs)
Current: 320+ lines doing: ID generation, task building, DB creation, API calls, Redis subscription.
Refactor:
initiateMiningId() - generates IDs and stream names
buildTaskObject() - constructs the MiningTask with processes
createDbRecords() - creates Supabase task records
startFetchJob() - calls email fetcher API
Why: When fetch fails mid-method, you hunt through 300 lines to find where.
4. Type-safe progress updates (~1 hr)
Current: Line 544 uses as Record<string, number> casting - hides type errors.
Refactor:
// From:
const taskProgress = process[taskProperty].details.progress as Record<string, number>;
// To:
const progressTypes: Record<TaskProgressType, keyof TaskProgress> = {
fetched: 'fetched',
extracted: 'extracted',
// ...
};
// Use with type guard
Why: Catches bugs at compile time, not runtime.
Files to Modify
backend/src/services/tasks-manager/TasksManager.ts
Files to Add
backend/src/services/tasks-manager/TaskProgressHandler.ts (new)
Files to Test
backend/test/unit/taskManager.test.ts - May need updates for extracted classes
Not in Scope
Success Criteria
- TasksManager under 500 lines
hasCompleted() uses flat method calls
createTask() broken into 4 clear steps
- No `as` casting on progress updates
Problem
The
TasksManagerclass (723 lines) is difficult to maintain due to:hasCompleted()(8 levels)createTask()at 320+ lines doing too muchThese hurt debugging and onboarding when things break at 3am.
Plan
1. Extract inline PubSub handler (~1-2 hrs)
Current: Lines 56-108 in constructor - JSON parsing, validation, state updates all together.
Refactor:
TaskProgressHandlerclassWhy: Easier to debug when PubSub fails, testable in isolation.
2. Break up `hasCompleted()` method (~2-3 hrs)
Current: 562-650 - 8 levels of nested conditionals, impossible to trace which process stops when.
Refactor:
Why: When a fetch task doesn't stop as expected, you can debug one method instead of unwinding 8 levels.
3. Split `createTask()` method (~2-3 hrs)
Current: 320+ lines doing: ID generation, task building, DB creation, API calls, Redis subscription.
Refactor:
initiateMiningId()- generates IDs and stream namesbuildTaskObject()- constructs the MiningTask with processescreateDbRecords()- creates Supabase task recordsstartFetchJob()- calls email fetcher APIWhy: When fetch fails mid-method, you hunt through 300 lines to find where.
4. Type-safe progress updates (~1 hr)
Current: Line 544 uses
as Record<string, number>casting - hides type errors.Refactor:
Why: Catches bugs at compile time, not runtime.
Files to Modify
backend/src/services/tasks-manager/TasksManager.tsFiles to Add
backend/src/services/tasks-manager/TaskProgressHandler.ts(new)Files to Test
backend/test/unit/taskManager.test.ts- May need updates for extracted classesNot in Scope
Success Criteria
hasCompleted()uses flat method callscreateTask()broken into 4 clear steps