# How the ADHD onEvent Callback Streams Progress and Events

> Discover how the ADHD onEvent callback streams real-time progress and monitors operations across its four-phase execution pipeline. Understand eight distinct event types.

- Repository: [Udit Akhouri/adhd](https://github.com/UditAkhourii/adhd)
- Tags: how-to-guide
- Published: 2026-08-01

---

**The ADHD `onEvent` callback streams real-time progress through eight distinct event types emitted during the four-phase execution pipeline in [`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts), enabling non-blocking monitoring of reframing, framing, scoring, clustering, and deepening operations.**

The `onEvent` callback is a core mechanism in the ADHD repository (UditAkhourii/adhd) for observing the internal state of the creative engine without blocking the main execution flow. By passing an optional callback to the `run` function, developers can receive structured progress notifications as the system transforms an input problem into clustered, deepened ideas.

## The RunEvent Type and Event Schema

The complete event contract is declared in **[`src/types.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/types.ts)**, where the `RunEvent` type defines every possible notification the engine can emit. Each event is a plain object with a `kind` discriminator and a payload specific to that phase.

The engine emits the following event kinds during a standard run:

- **`reframe:done`** – Emitted after optional anchor-stripping, carrying `{ changed: boolean }` to indicate whether the problem text was modified.
- **`frame:start`** – Fired before diverging a new frame, including `{ frameId: string; frameLabel: string }`.
- **`frame:done`** – Sent after a frame’s ideas are generated, reporting `{ frameId: string; count: number }`.
- **`score:done`** – Indicates all ideas have been scored, passing `{ total: number }`.
- **`cluster:done`** – Signals completion of clustering with `{ clusters: number }`.
- **`deepen:start`** – Marks the beginning of a deepening pass for a top-K idea, carrying `{ ideaId: string; text: string }`.
- **`deepen:done`** – Confirms completion of deepening for a specific idea with `{ ideaId: string }`.
- **`warn`** – Used by internal helpers to surface non-fatal issues such as API throttling, containing `{ message: string }`.

## Four-Phase Progress Streaming

The `run` function in **[`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts)** orchestrates execution through four distinct phases, invoking `onEvent` at major transition points to create a lightweight streaming protocol.

### Phase 0: Reframe

If `stripAnchors` is enabled in `RunOptions`, the engine first calls `reframeProblem` to clean the input. Immediately after this optional step, the callback receives:

```typescript
onEvent?.({ kind: "reframe:done", changed: true });

```

This occurs at lines 46–47 of [`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts), allowing callers to log whether the original problem was modified before processing continues.

### Phase 1: Diverge (Frames)

During the divergence phase, the engine iterates over selected frames and generates raw ideas for each. For every frame, the callback is invoked twice:

1. **Before generation** – `frame:start` is emitted at line 56, right before `divergeBranch` is called.
2. **After generation** – `frame:done` is emitted at line 58, reporting the quantity of ideas produced by that frame.

This creates a nested progress indicator showing which cognitive frame is active and how productive it was.

### Phase 2: Score and Cluster

Once all frames have diverged, the engine batches the ideas through scoring and clustering. At line 77, after `scoreIdeas` completes, the engine emits:

```typescript
onEvent?.({ kind: "score:done", total: ideas.length });

```

Immediately following the clustering operation at line 78, it emits:

```typescript
onEvent?.({ kind: "cluster:done", clusters: clusters.length });

```

These events provide quantitative milestones for tracking dataset reduction as the engine moves from broad ideation to focused synthesis.

### Phase 3: Deepen (Focus)

The final phase deepens the top-K ranked ideas. For each selected idea, the engine wraps the `deepenIdea` call with start and done events at lines 102 and 104:

```typescript
onEvent?.({ kind: "deepen:start", ideaId: idea.id, text: idea.text });
// ... deepening logic ...
onEvent?.({ kind: "deepen:done", ideaId: idea.id });

```

This pairing allows interfaces to display "focus" indicators or spinners while individual ideas are being expanded, creating a responsive user experience during the longest-running sub-operation.

## Implementing the onEvent Callback

To consume the progress stream programmatically, provide a function matching the `onEvent` signature when calling `run`:

```typescript
import { run, RunEvent } from "adhd";

const result = await run({
  problem: "How to improve remote collaboration?",
  stripAnchors: true,
  onEvent: (e: RunEvent) => {
    switch (e.kind) {
      case "frame:start":
        console.log(`Exploring frame: ${e.frameLabel}`);
        break;
      case "deepen:done":
        console.log(`Completed deep-dive on idea ${e.ideaId}`);
        break;
      case "warn":
        console.error(`Warning: ${e.message}`);
        break;
    }
  },
});

```

If `onEvent` is omitted or set to `undefined`, the engine runs silently, making the callback ideal for both verbose CLI tools and silent server-side batch processing.

## CLI Integration Example

The command-line interface in **[`src/cli.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/cli.ts)** demonstrates production usage of the callback for human-readable progress output. When the `--quiet` flag is absent, the CLI wires `onEvent` to `stderr` with concise status indicators:

```typescript
const onEvent = flags.quiet ? undefined : (e: RunEvent) => {
  switch (e.kind) {
    case "reframe:done": 
      if (e.changed) process.stderr.write(`  ↺ anchors stripped from problem\n`);
      break;
    case "frame:start":   
      process.stderr.write(`  ▸ ${e.frameLabel}…\n`);
      break;
    case "frame:done":    
      process.stderr.write(`    ${e.count} ideas (${e.frameId})\n`);
      break;
    case "score:done":    
      process.stderr.write(`  scored ${e.total} ideas\n`);
      break;
    case "cluster:done":  
      process.stderr.write(`  ${e.clusters} clusters\n`);
      break;
    case "deepen:start":  
      process.stderr.write(`  ◎ focus → ${e.text}\n`);
      break;
    case "warn":          
      process.stderr.write(`  ! ${e.message}\n`);
      break;
  }
};

```

This implementation (lines 20–30 of [`src/cli.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/cli.ts)) maps each `RunEvent` to a single terminal line, providing real-time visibility into the engine's four-phase pipeline without cluttering `stdout`, which remains reserved for the final JSON result.

## Summary

- The **ADHD `onEvent` callback** is defined in [`src/types.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/types.ts) as `(e: RunEvent) => void` and passed via `RunOptions` to the `run` function in [`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts).
- The engine emits **eight distinct event types** across four execution phases: reframing, framing (divergence), scoring/clustering, and deepening.
- **Phase-specific payloads** provide contextual data such as frame labels, idea counts, cluster totals, and warning messages.
- The callback is **fully optional**; when undefined, the engine suppresses all progress notifications for silent operation.
- The **CLI implementation** in [`src/cli.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/cli.ts) serves as a reference for mapping `RunEvent` objects to user-facing progress indicators.

## Frequently Asked Questions

### What is the TypeScript signature of the onEvent callback in ADHD?

The `onEvent` callback is typed as `(e: RunEvent) => void` and is passed as an optional property within the `RunOptions` object to the `run` function exported from [`src/engine.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/engine.ts). The `RunEvent` type is a discriminated union defined in [`src/types.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/types.ts) that includes variants for reframing, framing, scoring, clustering, deepening, and warnings.

### Which events are emitted during the framing phase?

During the framing phase, the engine emits `frame:start` immediately before calling `divergeBranch` for a specific frame, and `frame:done` immediately after the frame's ideas have been generated. These events include the `frameId` and `frameLabel` (on start) and the `count` of ideas produced (on done), allowing precise tracking of which cognitive frames are active and their productivity.

### How does the CLI handle quiet mode with onEvent?

The CLI in [`src/cli.ts`](https://github.com/UditAkhourii/adhd/blob/main/src/cli.ts) conditionally sets `onEvent` to `undefined` when the `--quiet` flag is present. When quiet mode is disabled, it provides a handler that writes formatted progress messages to `stderr`, ensuring that `stdout` remains clean for the final JSON output while still providing human-readable status updates during execution.

### Can I use onEvent for logging or analytics?

Yes, the `onEvent` callback is designed for observation without side effects on the core engine logic. You can use it to write structured logs to a file, send telemetry to an analytics service, or update a progress bar in a GUI application. Because the callback is invoked synchronously at phase boundaries, it should remain lightweight to avoid blocking the generation pipeline.