# Effect Query Primitives: Type-Safe Communication Across the Client-ACN Boundary

> Discover Effect Query primitives for type-safe client-ACN communication. Learn how these reactive abstractions revolutionize RPC transport in Magnitude.

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

---

**Effect Query primitives provide a unified, reactive abstraction layer—consisting of `Query`, `Mutation`, `Subscription`, and `Result`—that replaces traditional RPC transport and enables type-safe communication between Magnitude clients and the Agent Control Node (ACN).**

Effect Query primitives form the foundational communication protocol for all client-to-ACN (Agent Control Node) interactions in the Magnitude open-source framework. Unlike conventional RPC architectures that require separate transport implementations, these primitives define operations as pure, reusable values that materialize into reactive, cache-backed atoms through a single connection-scoped client. This approach consolidates data fetching, command execution, and real-time streaming into one authoritative boundary layer that maintains type safety from protocol definition to network transmission.

## The Four Core Effect Query Primitives

According to the specification in [`packages/effect-query/README.md`](https://github.com/magnitudedev/magnitude/blob/main/packages/effect-query/README.md), Effect Query exposes four composable primitives that cover all client-ACN interaction patterns. These are implemented in [`packages/effect-query/src/Operation.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/effect-query/src/Operation.ts) as typed Effect wrappers that carry cache policies, error schemas, and transport metadata.

### Query.make for Read Operations

**`Query.make`** declares read-only fetch operations using a typed key, an Effect that performs the data retrieval, and granular cache policies. Each query definition includes:

- A unique string key for cache identification
- **Payload** and **success** schemas for type-safe inputs and outputs
- **Error** schema for failure cases
- Cache configuration via `staleTime` (how long data remains fresh) and `gcTime` (garbage collection window)

```typescript
import { Query } from "@magnitudedev/effect-query";
import * as Schema from "@effect/schema/Schema";

const GetSession = Query.make("GetSession", {
  payload: { sessionId: Schema.String },
  success: SessionSchema,
  error: SessionError,
  staleTime: "30 seconds"
});

```

### Mutation.make for State Changes

**`Mutation.make`** defines write or command operations that modify remote state. Beyond the basic payload and success schemas, mutations support:

- **Scope** declarations that identify which cached entities the mutation affects
- A **synchronize** Effect that automatically invalidates or updates related queries upon completion

```typescript
import { Mutation, QueryClient } from "@magnitudedev/effect-query";

const RenameModel = Mutation.make("RenameModel", {
  payload: { modelId: Schema.String, name: Schema.String },
  success: Schema.Struct({}),
  error: ModelError,
  scope: ({ modelId }) => Mutation.MutationScope(`model:${modelId}`),
  synchronize: (_, { modelId }) =>
    QueryClient.invalidate(GetModel.match({ modelId }))
});

```

### Subscription.make for Real-Time Streams

**`Subscription.make`** creates keyed, reconnecting event streams that bridge server-side changes to client-side cache invalidations. Subscriptions accept:

- A payload schema (often empty for broadcast streams)
- Reconnection logic via Effect schedules
- Success schemas that define the shape of incoming events

```typescript
import { Subscription } from "@magnitudedev/effect-query";
import * as Schedule from "effect/Schedule";

const StreamChanges = Subscription.make("StreamChanges", {
  payload: Schema.Struct({}),  // no input required
  success: Schema.Struct({ query: Schema.String }),
  reconnect: Schedule.exponential("100 millis")
});

```

### Result for Status Tracking

**`Result`** is the typed container that represents the current lifecycle state of any query or mutation. It exposes four distinct statuses—`initial`, `loading`, `failure`, and `success`—enabling UI components to react to network states without manual flag management. The Result primitive ensures that every observation through the client cache reflects the authoritative, latest known state.

## Effect Query Primitives at the Client-ACN Boundary

The client-ACN boundary in Magnitude is architected as a three-stage pipeline that transforms abstract primitive definitions into executable, cached operations. This design is documented in [`design/cross-boundary/client-acn-icn-ownership.md`](https://github.com/magnitudedev/magnitude/blob/main/design/cross-boundary/client-acn-icn-ownership.md) and [`design/patterns/query-atom-abstraction.md`](https://github.com/magnitudedev/magnitude/blob/main/design/patterns/query-atom-abstraction.md).

### Defining the Protocol with Group.make

The boundary begins with a **protocol definition** that groups related primitives into namespaces using `Group.make`. This creates a type-safe contract that describes all possible operations across the client-ACN divide without binding to a specific transport mechanism.

```typescript
import { Group } from "@magnitudedev/effect-query";

const SessionGroup = Group.make({ GetSession });
const ChangesGroup = Group.make({ StreamChanges });

export const AcnBoundary = Group.make({
  Sessions: SessionGroup,
  Changes: ChangesGroup
});

```

Because these definitions are pure values, the same `AcnBoundary` specification can generate both RPC-backed and HTTP-API-backed adapters without code duplication.

### Adapting to Transport with RPC Layers

The second stage introduces the **RPC adapter** that converts primitive definitions into concrete client-server pairs. Magnitude provides `RpcAdapter.make()` to create transport-specific implementations that plug into the Effect-TS RPC system.

```typescript
import * as RpcAdapter from "@magnitudedev/effect-query/rpc";

export const AcnRpc = RpcAdapter.make();

```

This adapter layer handles serialization, request routing, and error marshaling while preserving the type contracts established by the primitive definitions.

### Materializing the AgentClient

The final stage **materializes** the connection-scoped client through `Client.make`, which combines the protocol definition with the transport layer. The resulting **AgentClient**—instantiated in [`packages/client-common/src/state/agent-client.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/client-common/src/state/agent-client.ts)—owns the Atom-based cache, mutation history, and coordination of in-flight work.

```typescript
const client = Client.make(
  AcnBoundary,
  AcnRpc.layer(AcnBoundary)  // installs the RPC transport
);

```

Once materialized, the client exposes callable atoms that correspond to the original primitive definitions:

```typescript
const sessionAtom = client.Sessions.GetSession({ sessionId: "abc" });
const renameAtom = client.Models.RenameModel;
const changesAtom = client.Changes.StreamChanges({});

```

### Cache Coordination and Single Source of Truth

Because the **AgentClient** holds the reactive cache, every observation (such as a React component using `useAtomValue`) and every mutation automatically share the same authoritative state. The boundary thus functions as a **single source of truth** for both data fetching and command execution.

When a mutation executes, its `synchronize` effect propagates changes through the cache, triggering updates in all subscribed observers without requiring manual cache key management or additional network requests. This centralized coordination eliminates race conditions and ensures consistency across the client-ACN boundary.

## Summary

- **Effect Query primitives** (`Query`, `Mutation`, `Subscription`, `Result`) replace traditional RPC layers with type-safe, reactive abstractions defined in [`packages/effect-query/src/Operation.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/effect-query/src/Operation.ts).
- **Pure value definitions** allow the same protocol specification to power multiple transport adapters (RPC or HTTP) without code duplication.
- The **client-ACN boundary** consists of three stages: protocol definition via `Group.make`, transport adaptation via `RpcAdapter.make`, and materialization via `Client.make`.
- The **AgentClient** (defined in [`packages/client-common/src/state/agent-client.ts`](https://github.com/magnitudedev/magnitude/blob/main/packages/client-common/src/state/agent-client.ts)) maintains an Atom-based cache that serves as the single source of truth for all client-ACN communication.
- **Automatic cache coordination** through mutation `synchronize` effects and subscription-driven invalidations keeps distributed state consistent without manual intervention.

## Frequently Asked Questions

### What is the difference between Query.make and Mutation.make in Effect Query?

**`Query.make`** defines read-only operations that fetch data and cache results based on `staleTime` and `gcTime` policies, while **`Mutation.make`** defines write operations that execute commands and can automatically synchronize related queries through the `synchronize` effect. Queries are typically used for data retrieval, whereas mutations handle state changes and side effects.

### How does the AgentClient maintain state consistency across the client-ACN boundary?

The **AgentClient** instantiates a connection-scoped cache using Effect-TS atoms, as detailed in [`design/patterns/query-atom-abstraction.md`](https://github.com/magnitudedev/magnitude/blob/main/design/patterns/query-atom-abstraction.md). When mutations complete, their configured `synchronize` effects invalidate or update specific cache keys, causing all subscribed observers to receive the updated state automatically. This reactive pattern ensures that all components sharing the client instance see consistent data without manual cache manipulation.

### Can Effect Query primitives work with transports other than RPC?

Yes. Because primitives are defined as pure values using `Group.make`, they are transport-agnostic. The same boundary definition can be materialized with `AcnRpc.layer()` for RPC communication or adapted to HTTP-based transports by swapping the adapter layer passed to `Client.make`. This architecture separates protocol semantics from transport mechanics, enabling reuse across different network environments.

### What does the Result primitive track in Effect Query?

The **Result** primitive tracks the execution lifecycle of queries and mutations through four distinct statuses: `initial` (before execution), `loading` (during network activity), `failure` (when errors occur), and `success` (when data returns). This container enables type-safe pattern matching on operation states and powers the reactive updates observed in UI components consuming the cache.