# How Magnitude Uses WorkingState and TaskGraph Projections to Track Tasks, Proposals, and Artifacts

> Discover how Magnitude leverages WorkingState and TaskGraph projections for robust tracking of tasks, proposals, and artifacts. Learn about its immutable, event-sourced architecture.

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

---

**Magnitude's runtime relies on two immutable, event-sourced projections—WorkingState and TaskGraph—to maintain a coherent, replayable view of agent activity and task execution.**

The **WorkingState** and **TaskGraph** projections form the backbone of Magnitude's state management, enabling precise tracking of what work exists, who is executing it, and when sessions complete. This architecture, built on immutable snapshots and deterministic event handling, powers everything from UI timelines to daemon lifecycle decisions.

## What Are Projections in Magnitude?

Projections in Magnitude are **read-only, deterministic views** derived from a stream of events. Unlike mutable state, projections can be replayed from any checkpoint to reconstruct exact session state—critical for debugging, reproducibility, and collaborative editing. The runtime maintains multiple specialized projections that each handle a slice of domain logic.

## The WorkingState Projection: Tracking Agent Activity

The **WorkingState projection** (formally `AgentLifecycleProjection`) monitors whether agents are idle or actively processing tasks, including aggregate counts across child agents.

### Core Implementation

Located in [`packages/agent/src/projections/agent-lifecycle.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/projections/agent-lifecycle.ts), this projection manages a **discriminated union** called `SessionWorkStatus`:

```typescript
type SessionWorkStatus =
  | { _tag: 'Working'; workerCount: number }
  | { _tag: 'Idle' };

```

When an agent begins work, the projection emits the `agentBecameWorking` signal and increments counters. The `countWorkingChildren(state, parentForkId)` helper traverses the fork hierarchy to report active child agents.

### Key Use Cases

- **UI feedback**: The timeline view displays "Working on it" based on this status
- **Daemon coordination**: Suppresses new task generation until `workerCount` reaches zero
- **Session lifecycle**: Determines when a session can be considered finished

## The TaskGraph Projection: Modeling Task Hierarchies

The **TaskGraph projection** maintains the complete tree of tasks—proposals, artifacts, and subtasks—created during a session. It stores metadata including status, assignees, worker bindings, and timestamps.

### State Structure

In [`packages/agent/src/projections/task-graph.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/projections/task-graph.ts), the projection holds:

```typescript
interface TaskGraphState {
  tasks: ReadonlyMap<string, TaskRecord>;
  rootTaskIds: string[];
}

```

### Event Handlers and Signals

The projection responds to domain events with immutable updates:

| Event | Signal Emitted | Effect |
|-------|---------------|--------|
| `task_created` | `taskCreated` | Adds task to `tasks` Map, updates `rootTaskIds` if root-level |
| `task_updated` | `taskStatusChanged` | Merges updates into existing `TaskRecord` |
| `task_assigned` | `taskAssigned` | Links worker to task |
| `task_cancelled` | `taskCancelled` | Marks task as cancelled |

### Query Helpers

The projection exports pure functions for graph traversal:

- `collectSubtreeTaskIds(state, taskId)` – gathers all descendant task IDs
- `canCompleteTask(state, taskId)` – checks completion preconditions
- `reparentTask(state, taskId, newParentId)` – moves tasks within the hierarchy

## How the Projections Work Together

The `TaskAssignmentProjection` (defined in [`packages/agent/src/projections/task-assignment.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/projections/task-assignment.ts)) bridges WorkingState and TaskGraph, coordinating their updates:

1. **Task creation** – `TaskGraphProjection` receives `task_created`, emits `taskCreated`
2. **Worker assignment** – `TaskAssignmentProjection` updates the task's `worker` field and calls `markWorkerWorking` on `AgentLifecycleProjection`
3. **Working status propagation** – `AgentLifecycleProjection` emits `agentBecameWorking`; UI and daemon react
4. **Task completion** – `TaskGraphProjection` sets `status: 'completed'`, emits `taskCompleted` and `taskStatusChanged`; `AgentLifecycleProjection` re-evaluates `countWorkingChildren` and may transition to `Idle`

## Practical Usage Examples

### Querying Pending Root Tasks

```typescript
import { TaskGraphProjection } from '@magnitudedev/agent/src/projections/task-graph';
import { Effect } from 'effect';

const pendingRootTasks = Effect.gen(function* () {
  const tg = yield* Effect.service(TaskGraphProjection.Tag);
  const state = yield* tg.read;
  const pending = state.rootTaskIds
    .map(id => state.tasks.get(id)!)
    .filter(t => t.status === 'pending');
  return pending;
});

```

### Checking Session Working Status

```typescript
import { AgentLifecycleProjection } from '@magnitudedev/agent/src/projections/agent-lifecycle';

const isSessionWorking = Effect.gen(function* () {
  const al = yield* Effect.service(AgentLifecycleProjection.Tag);
  const state = yield* al.read;
  return state._tag === 'Working';
});

```

## Key Source Files

| File | Purpose |
|------|---------|
| [`packages/agent/src/projections/agent-lifecycle.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/projections/agent-lifecycle.ts) | `AgentLifecycleProjection`, `SessionWorkStatus`, working-state signals |
| [`packages/agent/src/projections/task-graph.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/projections/task-graph.ts) | `TaskGraphProjection`, immutable task tree, lifecycle event handlers |
| [`packages/agent/src/projections/task-assignment.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/projections/task-assignment.ts) | Bridges projections, manages worker-task bindings |
| [`packages/agent/src/tools/task-reader.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/tools/task-reader.ts) | `TaskGraphStateReaderTag` service for external queries |
| [`packages/agent/src/display/timeline-projection.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/display/timeline-projection.ts) | Consumes both projections for UI rendering |

## Summary

- **WorkingState projection** tracks agent lifecycle state (idle vs. working) with hierarchical child counting via `AgentLifecycleProjection`
- **TaskGraph projection** maintains the immutable task tree with full metadata and provides graph query utilities
- **TaskAssignmentProjection** coordinates between them, ensuring working status reflects actual task assignments
- Both projections emit typed signals that drive UI updates and daemon behavior
- The event-sourced, immutable design enables deterministic replay for debugging and reproducibility

## Frequently Asked Questions

### What is a projection in Magnitude's architecture?

A projection is an immutable, deterministic read model derived from event streams. Magnitude uses projections to maintain specialized views of system state without mutating shared data, enabling safe replay and parallel processing.

### How does Magnitude know when a session is finished?

The `AgentLifecycleProjection` evaluates `countWorkingChildren` across the fork hierarchy. When no child agents report working status, the `SessionWorkStatus` transitions to `Idle`, signaling completion to the daemon and UI.

### Can I query the task graph from custom tools?

Yes. Import `TaskGraphStateReaderTag` from [`packages/agent/src/tools/task-reader.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/agent/src/tools/task-reader.ts) to inject a reader service. This provides read-only access to `TaskGraphState` without exposing mutation capabilities.

### What happens when a task is reparented in the graph?

The `reparentTask` helper in `TaskGraphProjection` produces a new immutable state with updated parent references. The original state remains unchanged, preserving history and enabling time-travel debugging.