# How Magnitude Event‑Core Projections Enable Session Replay: Window, Fork, and TaskGraph Explained

> Learn how Magnitude's event-core projections like Window, Fork, and TaskGraph enable session replay by reconstructing past states from immutable event streams.

- Repository: [Magnitude/magnitude](https://github.com/magnitudedev/magnitude)
- Tags: deep-dive
- Published: 2026-09-06

---

**Magnitude's event‑core package uses immutable event streams and deterministic projections—including Window, Fork, and TaskGraph types—to reconstruct any point‑in‑time session state for reliable replay.**

The **event‑core** system in [magnitudedev/magnitude](https://github.com/magnitudedev/magnitude) implements event‑sourcing architecture where **projections** act as pure reducers that materialize read‑only views from event streams. Understanding how **event‑core projections** work is essential for building debuggable, replayable applications—particularly for AI workflows requiring complex state inspection.

## Core Projection Types in Event‑Core

Magnitude provides three specialized projection patterns that enable different replay scenarios. Each type lives in `packages/event-core/src/projection/` and serves distinct use cases for session reconstruction.

### Regular Projections with Projection.define

The foundation of the system is `Projection.define`, declared in [[`define.ts`](https://github.com/magnitudedev/magnitude/blob/main/define.ts)](https://github.com/magnitudedev/magnitude/blob/main/packages/event-core/src/projection/define.ts). This creates **non‑forked projections** that maintain a single state value across all events.

A regular projection specifies:

- The **event types** it consumes
- An **initial state schema**
- **Event handler functions** for state mutation

```typescript
import { Projection } from '@magnitudedev/event-core';

type IncrementEvent = { type: 'increment'; amount: number };

const CounterProjection = Projection.define<IncrementEvent>()({
  name: 'Counter',
  init: () => ({ count: 0 }),
  handlers: {
    increment: (state, ev) => ({ count: state.count + ev.amount }),
  },
});

```

The handlers are **pure functions**—they receive the current state and event, returning new state without side effects. This determinism guarantees identical replay results.

### Forked Projections with Projection.defineForked

Parallel workflows require **fork‑aware projections** via `Projection.defineForked` in [[`defineForked.ts`](https://github.com/magnitudedev/magnitude/blob/main/defineForked.ts)](https://github.com/magnitudedev/magnitude/blob/main/packages/event-core/src/projection/defineForked.ts). These maintain **independent state per fork ID**, enabling scenarios like:

- User‑opened branches
- Parallel AI agent workflows
- Temporary sandboxed contexts

```typescript
import { Projection } from '@magnitudedev/event-core';
import type { ForkWindowState } from '@magnitudedev/event-core';

type MyEvent = { type: 'message'; text: string; forkId: string | null };

const WindowProjection = Projection.defineForked<MyEvent, ForkWindowState>()({
  name: 'Window',
  init: () => ({ messages: [] }),
  handlers: {
    message: (state, ev) => ({
      messages: [...state.messages, ev.text],
    }),
  },
});

```

Forked projections expose `getFork(forkId)` and `allForks` for accessing specific branches. The replay engine routes events to the appropriate fork based on each event's `forkId` field.

### TaskGraph Projections for DAG Workflows

The **TaskGraph projection**—conceptually located at [`task-graph.ts`](https://github.com/magnitudedev/magnitude/blob/main/task-graph.ts) alongside other projection definitions—represents **directed acyclic graphs of tasks**. This enables replay of multi‑stage AI pipelines where tasks have dependencies and completion states.

```typescript
type TaskEvent = { type: 'taskStarted' | 'taskFinished'; id: string; deps: string[] };

const TaskGraphProjection = Projection.define<TaskEvent>()({
  name: 'TaskGraph',
  init: () => ({ nodes: new Map<string, TaskNode>() }),
  handlers: {
    taskStarted: (state, ev) => {
      state.nodes.set(ev.id, { id: ev.id, deps: ev.deps, status: 'running' });
    },
    taskFinished: (state, ev) => {
      const node = state.nodes.get(ev.id);
      if (node) node.status = 'completed';
    },
  },
});

```

Each node stores its **dependencies and status**, allowing the engine to reconstruct the exact execution graph at any point during replay.

## Window Projections: Addressed Sequence Access

**Window projections** provide **addressed sequence windows**—contiguous slices of ordered event streams without scanning entire logs. The implementation in [[`addressed/collections/sequence.ts`](https://github.com/magnitudedev/magnitude/blob/main/addressed/collections/sequence.ts)](https://github.com/magnitudedev/magnitude/blob/main/packages/event-core/src/addressed/collections/sequence.ts) exposes three key helpers:

| Helper | Purpose |
|--------|---------|
| `resolveRangeWindow` | Get a specific index range |
| `resolveTailWindow` | Get the last N items |
| `readWindow` | Materialize the window from state |

```typescript
import { addressed } from '@magnitudedev/event-core';

// Get last 10 messages from replayed state
const tailWindow = addressed.messages.resolveTailWindow(state.messages, 10);
const recentMessages = yield* addressed.messages.readWindow(tailWindow);

```

Windows are **computed logically** rather than by event log traversal. This keeps memory usage bounded during replay and enables efficient state inspection.

## How Session Replay Works Step‑by‑Step

The replay engine in [[`engine.ts`](https://github.com/magnitudedev/magnitude/blob/main/engine.ts)](https://github.com/magnitudedev/magnitude/blob/main/packages/event-core/src/engine.ts) orchestrates deterministic reconstruction. Here's the complete flow:

### 1. Event Ingestion and State Mutation

The `EventBus` publishes immutable events. Each projection—Window, Fork, or TaskGraph—registers as a consumer and updates its **in‑memory state** through pure handlers.

### 2. Snapshot Persistence

After configurable checkpoints, the engine writes **projection snapshots** containing:

- Projection identifier
- Fork ID (if applicable)
- Serialized state via `Schema.encode`
- Event index the snapshot represents

### 3. Replay Execution

When reconstructing state, the engine:

1. Loads the latest snapshot **≤ target index**
2. Instantiates fresh projection instances with snapshot state
3. Re‑applies events from `snapshot.index + 1` to target index

```typescript
import { engine } from '@magnitudedev/event-core';

const snapshot = await engine.loadSnapshot('session-1', 120);
const plan = await engine.prepareProjectionSnapshotRestore(snapshot, { targetIndex: 250 });
await plan.run(); // All projections now at index 250

```

This pattern—demonstrated in [[`invariants.test.ts`](https://github.com/magnitudedev/magnitude/blob/main/invariants.test.ts)](https://github.com/magnitudedev/magnitude/blob/main/packages/event-core/src/projection/invariants.test.ts)—ensures **deterministic reconstruction** regardless of projection type.

### 4. Fork Access During Replay

For forked projections, access specific branches via:

```typescript
const forkId = 'branch-42';
const forkInst = projectionInstance.getFork(forkId);
const forkState = yield* forkInst.read();

```

The engine recreates each fork's state independently from its snapshot.

## Why This Design Enables Reliable Session Replay

**Deterministic reducers** eliminate side effects that could corrupt replay. The same event sequence always produces identical projection state.

**Snapshot granularity** minimizes replay overhead. Jumping to a near‑target checkpoint means processing only a small event tail.

**Fork isolation** prevents cross‑branch interference. Parallel workflows replay independently through separate state dictionaries.

**TaskGraph expressiveness** reconstructs not just linear state but **execution topology**—critical for debugging multi‑stage AI pipelines where task dependencies matter.

## Summary

- **Projection.define** creates regular projections with single state values in [`define.ts`](https://github.com/magnitudedev/magnitude/blob/main/define.ts)
- **Projection.defineForked** enables parallel branch isolation via `getFork`/`allForks` in [`defineForked.ts`](https://github.com/magnitudedev/magnitude/blob/main/defineForked.ts)
- **Window projections** provide memory‑efficient sequence slices through `resolveTailWindow` and helpers in [`addressed/collections/sequence.ts`](https://github.com/magnitudedev/magnitude/blob/main/addressed/collections/sequence.ts)
- **TaskGraph projections** model DAG workflows for complex pipeline replay
- **Snapshot + replay engine** in [`engine.ts`](https://github.com/magnitudedev/magnitude/blob/main/engine.ts) delivers deterministic point‑in‑time reconstruction from any checkpoint

## Frequently Asked Questions

### What makes Magnitude projections "pure" and why does that matter for replay?

A projection is **pure** when its handlers are deterministic functions of (state, event) → new state with no side effects. This matters because the replay engine in [`engine.ts`](https://github.com/magnitudedev/magnitude/blob/main/engine.ts) must guarantee that re‑executing the same event sequence produces identical state. Impure handlers—those with external I/O or randomness—would break this guarantee and corrupt session replay.

### How does the Window projection avoid loading entire event histories?

Window projections use **logical resolution** via `resolveRangeWindow` and `resolveTailWindow` in [`sequence.ts`](https://github.com/magnitudedev/magnitude/blob/main/sequence.ts). These helpers compute address descriptors—start index, end index, and slice parameters—without materializing data. Only `readWindow` touches actual state, and it reads from the **already‑projected in‑memory representation** rather than the event log. This keeps memory usage O(window size) rather than O(total events).

### When should I use defineForked versus define for my projection?

Use **defineForked** when your domain has **parallel execution contexts** that must not interfere: user branches, A/B test variants, or sandboxed agent runs. Each fork maintains independent state under the same projection logic. Use **define** for **global session state** that all contexts share—like an overall session status or cross‑fork metrics. The fork ID in events (`forkId: string | null`) determines routing at replay time.

### Can TaskGraph projections represent cyclic dependencies?

No—**TaskGraph projections intentionally model DAGs** (directed acyclic graphs). The `deps: string[]` field in task events establishes parent→child relationships. Cycles would create logical contradictions in replay: a task could not start until its dependency finished, yet that dependency depends on the original task. The projection handlers in the conceptual [`task-graph.ts`](https://github.com/magnitudedev/magnitude/blob/main/task-graph.ts) assume acyclicity for deterministic topological ordering during state reconstruction.