# How OpenWork Manages Background Tasks: Diagnostic Tracking and Graceful Shutdown

> Learn how OpenWork manages background tasks using a diagnostic tracker for monitoring async loops, error classification, and graceful shutdown with abort signals.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: internals
- Published: 2026-08-02

---

**OpenWork manages background tasks through a centralized diagnostic tracker that monitors async loop phases, classifies errors with structured metadata, and ensures graceful shutdown via abort signals and lifecycle hooks.**

OpenWork, an open-source enterprise platform from `different-ai/openwork`, relies on long-running background processes like MCP diagnostic polling and server-side event listeners to maintain system health. These asynchronous operations are managed through a robust **diagnostic tracker pattern** implemented in the `ExternalMcpDiagnosticTracker` class, which provides observability and error resilience across the codebase.

## The Three Pillars of Background Task Management

OpenWork's background task architecture rests on three core concepts that ensure reliability and observability.

### Centralized Diagnostic Tracking with ExternalMcpDiagnosticTracker

At the heart of the system is the **ExternalMcpDiagnosticTracker** class, defined in [`packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts`](https://github.com/different-ai/openwork/blob/main/packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts). This tracker records the current phase of a background operation, captures any errors that occur, and stores a unique `referenceId` that can be displayed to users in UI error banners.

The tracker's **`captureBackgroundFailure`** method serves as the primary entry point for error handling. When a background task throws an exception, this method immediately classifies the error and stores a structured `ExternalMcpDiagnosticError` object that includes safe-cause chains, HTTP status codes, and provider-declared error evidence.

### Graceful Lifecycle Hooks and Abort Signals

Background tasks are orchestrated through explicit lifecycle management in the Den API server. The system launches async loops during **`startServer`** and shuts them down cleanly via **`stopBackgroundLoops`**, located in [`ee/apps/den-api/src/server.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/server.ts).

The loops themselves are ordinary async functions that accept an **`AbortSignal`** and periodically check `abortSignal.aborted` to determine when to terminate. During shutdown, `stopBackgroundLoops()` iterates over all running loops, awaits their completion, and logs any failures via `appLogger.error("background loop shutdown failed",…)`.

### Structured Error Classification

Raw exceptions are transformed into user-friendly diagnostics through the **`classifyError`** function. This logic maps arbitrary errors into structured `Classification` objects that include phase context, error category, and retryability flags. The classification logic resides in the same diagnostic module as the tracker, ensuring consistent error handling across all background operations.

## The Background Task Execution Flow

Background tasks in OpenWork follow a strict lifecycle that maximizes observability and minimizes unhandled rejections.

### Initialization and Phase Declaration

When the server starts, it creates an `ExternalMcpDiagnosticTracker` instance for each logical background job, such as an "MCP tool-catalog poll". The tracker initializes in the `CONFIGURATION` phase and generates a unique `referenceId`. Before performing work, the task calls **`tracker.begin(phase)`** to announce the current sub-phase (e.g., `MCP_TOOL_DISCOVERY`) to the monitoring system.

### Success Tracking and Health Monitoring

Upon successful completion of an operation, the task invokes **`tracker.passed(phase, healthLevel)`**, passing a health level indicator such as `"catalog_ready"`. This updates the diagnostic state and confirms the task is functioning correctly, allowing the UI to display healthy status indicators.

### Failure Capture and User-Friendly Diagnostics

If the async work throws an exception, the catch-block immediately calls **`tracker.captureBackgroundFailure(phase, err)`**. The tracker automatically classifies the error using the internal `classifyError` switch statement, creating an `ExternalMcpDiagnosticError` with structured metadata. This prevents raw stack traces from reaching users while preserving diagnostic detail for operators.

## Implementation Example: Creating a Diagnostic-Tracked Polling Loop

The following pattern demonstrates how to implement a robust background polling loop using OpenWork's diagnostic infrastructure:

```typescript
import { ExternalMcpDiagnosticTracker } from "@openwork/enterprise-mcp-client";

// Create a tracker for this background task
const pollTracker = new ExternalMcpDiagnosticTracker("poll-mcp-tools");

// The actual loop – runs until `abortSignal` is triggered
async function pollMcpTools(abortSignal: AbortSignal) {
  while (!abortSignal.aborted) {
    pollTracker.begin("MCP_TOOL_DISCOVERY");
    try {
      const catalog = await fetchToolCatalog();          // ← network request
      pollTracker.passed("MCP_TOOL_DISCOVERY", "catalog_ready");
    } catch (err) {
      // Any failure is captured and classified automatically
      pollTracker.captureBackgroundFailure("MCP_TOOL_DISCOVERY", err);
      // Optionally break out if the error is not retryable
      if (!isRetryable(err)) break;
    }
    await delay(30_000); // wait 30 s before the next poll
  }
}

// Register the loop with the server’s background‑loop manager
startBackgroundLoop(pollMcpTools);

```

The tracker automatically enriches the diagnostic object with HTTP status codes and provider-specific error evidence, making it available for UI rendering or automated retry logic.

## Key Source Files in the OpenWork Repository

| File | Role |
|------|------|
| [`packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts`](https://github.com/different-ai/openwork/blob/main/packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts) | Core tracker class, `classifyError` logic, and background-failure helpers used by both client and server |
| [`ee/apps/den-api/src/capability-sources/external-mcp-diagnostics.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/capability-sources/external-mcp-diagnostics.ts) | Server-specific diagnostic module imports and implementations |
| [`ee/apps/den-api/src/server.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/server.ts) | Server bootstrap logic, `startServer` initialization, and `stopBackgroundLoops` shutdown orchestration |

## Summary

- **OpenWork treats every long-running process as a diagnostic-tracked background task** using the `ExternalMcpDiagnosticTracker` class.
- **Phase-aware monitoring** via `begin()` and `passed()` methods provides granular visibility into task execution states.
- **Automatic error classification** transforms raw exceptions into structured `ExternalMcpDiagnosticError` objects suitable for UI display and operational logging.
- **Graceful shutdown** is achieved through `AbortSignal` propagation and the `stopBackgroundLoops()` lifecycle hook in the Den API server.
- **File locations**: Core logic resides in [`packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts`](https://github.com/different-ai/openwork/blob/main/packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts) and server orchestration in [`ee/apps/den-api/src/server.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/server.ts).

## Frequently Asked Questions

### How does OpenWork prevent unhandled promise rejections in background tasks?

OpenWork wraps all background operations in `try-catch` blocks that route exceptions through `tracker.captureBackgroundFailure()`. This ensures every error is classified, stored with a `referenceId`, and surfaced to the UI as a structured diagnostic rather than an unhandled rejection or raw stack trace.

### What information does the ExternalMcpDiagnosticTracker store?

The tracker stores the current operational phase, a unique `referenceId` for UI correlation, health level indicators, and structured error classifications. It automatically captures HTTP status codes, safe-cause chains, and provider-declared error evidence when failures occur.

### How are background tasks shut down when the server receives SIGTERM?

The server invokes `stopBackgroundLoops()`, which iterates over all registered async loops and awaits their completion. Each loop monitors an `AbortSignal`, allowing cooperative cancellation. Any shutdown failures are logged via `appLogger.error()` before the process exits.

### Where is the error classification logic defined?

The **`classifyError`** function and `ExternalMcpDiagnosticError` class are defined in [`packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts`](https://github.com/different-ai/openwork/blob/main/packages/enterprise-mcp-client/src/capability-sources/external-mcp-diagnostics.ts). This module contains the switch-based classification logic that maps raw exceptions to structured diagnostic objects with retryability flags and category metadata.