Skip to content

refactor: Simplify Event System — replace overhead events with direct calls (Phase 1) #88

@dean0x

Description

@dean0x

Epic: #87 — Architectural Simplification v0.6.0

Goal

Remove ~22 overhead events, replace with direct calls. Keep events only for genuine fan-out (terminal-state handlers) and command triggers.

Sub-tasks

1a. Replace Query Events with Direct Calls

Problem: TaskManager uses eventBus.request(TaskStatusQuery)QueryHandlertaskRepo.findById(). This adds ~1ms latency per query and exists only for architectural purity.

Change: Inject TaskRepository and OutputCapture into TaskManagerService. Replace request() calls with direct repository calls.

Files:

  • src/services/task-manager.ts — Add taskRepo: TaskRepository and outputCapture: OutputCapture to constructor. Replace request(TaskStatusQuery) with taskRepo.findById(). Replace request(TaskLogsQuery) with outputCapture.getOutput().
  • src/bootstrap.ts — Pass taskRepository and outputCapture to TaskManagerService constructor.
  • src/core/events/events.ts — Remove TaskStatusQuery, TaskStatusResponse, TaskLogsQuery, TaskLogsResponse event types.
  • src/services/handlers/query-handler.tsRemove entirely (all functionality moved to direct calls).
  • src/services/handler-setup.ts — Remove QueryHandler from standard handlers list.
  • Tests: Remove query-handler tests, update task-manager tests.

Also: Replace eventBus.request(NextTaskQuery) in WorkerHandler with direct queue.dequeue() call.

  • src/services/handlers/worker-handler.ts — Inject TaskQueue, call queue.dequeue() directly.
  • src/core/events/events.ts — Remove NextTaskQuery event type.
  • src/services/handlers/queue-handler.ts — Remove NextTaskQuery subscription and handler.

1b. Linearize the Trigger Chain

Problem: TaskDelegatedPersistenceHandler.save() → emit TaskPersistedQueueHandler.enqueue() → emit TaskQueuedWorkerHandler.processNextTask(). This is a strictly sequential pipeline pretending to be event-driven.

Change: TaskManager.delegate() calls taskRepo.save() directly, then calls queue.enqueue() (via a new method or direct call), then triggers worker processing. The TaskDelegated event is still emitted for fan-out subscribers (DependencyHandler needs it).

Files:

  • src/services/task-manager.ts — After emitting TaskDelegated, call taskRepo.save(task) directly, then queue.enqueue(task) (if not blocked by dependencies).
  • src/services/handlers/persistence-handler.ts — Remove TaskDelegated subscription (TaskManager now saves directly). Keep terminal-state subscriptions (TaskStarted, TaskCompleted, TaskFailed, TaskCancelled, TaskTimeout).
  • src/services/handlers/queue-handler.ts — Remove TaskPersisted subscription. Add direct enqueueIfReady(task) method callable by TaskManager.
  • src/core/events/events.ts — Remove TaskPersisted event type.

Keep: TaskDelegated event (DependencyHandler subscribes), TaskQueued event (WorkerHandler subscribes — or make this a direct call too in 1c).

1c. Remove Informational-Only Events

Problem: OutputCaptured, WorkerSpawned, RecoveryStarted, RecoveryCompleted, SystemResourcesUpdated are consumed only for debug logging or advisory metrics.

Change: Replace with direct logger calls at the point of action. Remove event definitions and subscriptions.

Files:

  • src/core/events/events.ts — Remove the 5 event types listed above.
  • src/implementations/event-driven-worker-pool.ts — Remove WorkerSpawned emission (already logged locally).
  • src/implementations/output-capture.ts — Remove OutputCaptured emission.
  • src/services/recovery-manager.ts — Remove RecoveryStarted/RecoveryCompleted emissions (already logged).
  • src/services/handlers/output-handler.tsRemove entirely (only consumed OutputCaptured for debug logging).
  • src/services/autoscaling-manager.ts — Remove SystemResourcesUpdated subscription. If AutoscalingManager is advisory-only with no actionable behavior, consider removing it entirely.

1d. Resolve waitForCheckpoint Race Condition

Problem: DependencyHandler and CheckpointHandler both subscribe to TaskCompleted. DependencyHandler needs the checkpoint that CheckpointHandler creates, so it races with a subscription + DB lookup (lines 444-494 in dependency-handler.ts).

Change: With the hybrid model, make checkpoint creation explicit in the completion path. The completion fan-out calls CheckpointHandler.createCheckpoint() first, then DependencyHandler.resolveDependencies(). The race condition disappears.

Files:

  • src/services/handlers/dependency-handler.ts — Remove waitForCheckpoint() method and race logic. Accept checkpoint as a parameter or query it after guaranteed creation.

Files Changed

Modified

  • src/services/task-manager.ts
  • src/core/events/events.ts
  • src/services/handlers/worker-handler.ts
  • src/services/handlers/queue-handler.ts
  • src/services/handlers/persistence-handler.ts
  • src/services/handlers/dependency-handler.ts
  • src/services/handler-setup.ts
  • src/bootstrap.ts
  • src/implementations/event-driven-worker-pool.ts
  • src/implementations/output-capture.ts
  • src/implementations/resource-monitor.ts
  • src/services/recovery-manager.ts

Removed

  • src/services/handlers/query-handler.ts — Replaced by direct repo calls
  • src/services/handlers/output-handler.ts — Only consumed debug event
  • src/services/autoscaling-manager.ts — Advisory-only, no actionable behavior
  • tests/unit/services/handlers/query-handler.test.ts
  • tests/unit/services/handlers/output-handler.test.ts

Risk

Medium — touches core orchestration and task delegation flow. Sub-steps (1a → 1b → 1c → 1d) can be validated incrementally.

Verification

  • npm run build — clean compilation
  • npx biome check src/ tests/ — no lint issues
  • All test groups pass: core, handlers, services, repositories, adapters, CLI, integration
  • Event audit: grep -r "emit\|subscribe" src/ shows only ~15 retained events

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureArchitecture improvementenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions