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
workerCountreaches 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 IDscanCompleteTask(state, taskId)– checks completion preconditionsreparentTask(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:
- Task creation –
TaskGraphProjectionreceivestask_created, emitstaskCreated - Worker assignment –
TaskAssignmentProjectionupdates the task'sworkerfield and callsmarkWorkerWorkingonAgentLifecycleProjection - Working status propagation –
AgentLifecycleProjectionemitsagentBecameWorking; UI and daemon react - Task completion –
TaskGraphProjectionsetsstatus: 'completed', emitstaskCompletedandtaskStatusChanged;AgentLifecycleProjectionre-evaluatescountWorkingChildrenand may transition toIdle
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →