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

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, this projection manages a discriminated union called SessionWorkStatus:

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, the projection holds:

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) 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

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

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 AgentLifecycleProjection, SessionWorkStatus, working-state signals
packages/agent/src/projections/task-graph.ts TaskGraphProjection, immutable task tree, lifecycle event handlers
packages/agent/src/projections/task-assignment.ts Bridges projections, manages worker-task bindings
packages/agent/src/tools/task-reader.ts TaskGraphStateReaderTag service for external queries
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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →