# Context hierarchies

> SystemContext forms a parent-child tree that isolates per-agent state while sharing server-level resources.

`SystemContext` forms a parent-child tree that isolates per-agent state while sharing server-level resources. Every system runs against a context, and contexts can be nested to create scoped layers of state.

```text
Server (globals: AppConfig, ToolRegistry, ModelRegistry)
   │
   └── Agent Context (locals: AgentMemory)
          │
          └── Session Context (locals: ConversationHistory)
                 │
                 └── Turn Context (locals: Scratchpad)
```

This diagram illustrates a hierarchy you can construct; the sessions API does not automatically create all four levels. Each level in the tree owns its own local resources and can read from any ancestor. The server sits at the root, holding global resources that every context can reach.

## How the hierarchy is built

The server creates root contexts with `Server::create_context()`. Each root context gets its own fresh local resources (instantiated from the factories you registered) and a shared reference to the server’s global resources.

```rust
use polaris_ai::system::server::Server;
use polaris_ai::system::resource::{GlobalResource, LocalResource};


struct AppConfig { model_id: String }
impl GlobalResource for AppConfig {}


struct ConversationHistory { messages: Vec<String> }
impl LocalResource for ConversationHistory {}


let mut server = Server::new();
server.insert_global(AppConfig { model_id: "claude-sonnet-4-20250514".into() });
server.register_local(|| ConversationHistory { messages: Vec::new() });
server.finish().await?;


let agent_ctx = server.create_context();
```

The returned `SystemContext<'static>` is decoupled from the server’s lifetime because it holds an `Arc` to the global resources. You can store it, move it across threads, or hand it to an executor.

## Creating child contexts

Any context can spawn a child with `SystemContext::child()`. The child holds a read-only reference to its parent and inherits the same global resources handle. It starts with an empty local resource set and empty outputs.

```rust
use polaris_ai::system::param::SystemContext;
use polaris_ai::system::resource::LocalResource;


struct Scratchpad { notes: Vec<String> }
impl LocalResource for Scratchpad {}


let agent_ctx = server.create_context();


let mut turn_ctx = agent_ctx.child();
turn_ctx.insert(Scratchpad { notes: Vec::new() });
```

The `turn_ctx` can read everything in `agent_ctx` (and through it, the server’s globals), but it has its own `Scratchpad` that no other context can see. You can nest children arbitrarily deep, though in practice the typical depth is Server, Agent, Session, Turn.

## Resource lookup with Res

When a system requests `Res<T>`, the context searches for `T` in this order:

1. The current context’s local resources
2. The parent context’s local resources, then the grandparent’s, walking up the chain
3. The server’s global resources

The first match wins. A local resource in a child context shadows the same type in a parent context.

```rust
use polaris_ai::system::param::{Res, ResMut};
use polaris_ai::system::resource::{GlobalResource, LocalResource};
use polaris_ai::system::system;


struct AppConfig { agent_name: String }
impl GlobalResource for AppConfig {}


struct ConversationHistory { messages: Vec<String> }
impl LocalResource for ConversationHistory {}


#[system]
async fn summarize(
    config: Res<AppConfig>,
    history: Res<ConversationHistory>,
) -> String {
    format!("{}: {} messages so far", config.agent_name, history.messages.len())
}
```

In this system, `Res<AppConfig>` reaches all the way up to the server’s globals, while `Res<ConversationHistory>` resolves from whichever ancestor first has one. The system does not need to know where in the hierarchy each resource lives.

## Scope isolation with ResMut

`ResMut<T>` does not walk the hierarchy. It accesses only the current context’s local resources. This is a deliberate constraint: mutable access to a parent’s state from a child would break isolation between sibling contexts.

```rust
struct Scratchpad { notes: Vec<String> }
impl LocalResource for Scratchpad {}


#[system]
async fn take_notes(
    config: Res<AppConfig>,
    mut scratch: ResMut<Scratchpad>,
) {
    scratch.notes.push(format!("Turn started for {}", config.agent_name));
}
```

If `Scratchpad` exists in the current context, `ResMut<Scratchpad>` resolves to it. If it only exists in a parent context, the fetch fails with a `ParamError::ResourceNotFound`. This ensures that a turn context cannot mutate the session’s resources, and a session context cannot mutate the agent’s resources.

## Multi-agent pattern

The hierarchy makes multi-agent setups straightforward. Two agents share the same server globals (model registry, tool registry, configuration) but each gets its own local resources from the factory.

```rust
use polaris_ai::system::server::Server;
use polaris_ai::system::resource::{GlobalResource, LocalResource};


struct ToolRegistry { /* ... */ }
impl GlobalResource for ToolRegistry {}


struct AgentMemory { history: Vec<String> }
impl LocalResource for AgentMemory {}


let mut server = Server::new();
server.insert_global(ToolRegistry { /* ... */ });
server.register_local(|| AgentMemory { history: Vec::new() });
server.finish().await?;


let researcher_ctx = server.create_context();
let writer_ctx = server.create_context();
```

Both `researcher_ctx` and `writer_ctx` read from the same `ToolRegistry` through `Res<ToolRegistry>`, but each has its own `AgentMemory`. Mutations to one agent’s memory are invisible to the other.

Within each agent, you can create session and turn child contexts to add further layers of isolation:

```rust
let session_ctx = researcher_ctx.child();
let turn_one = session_ctx.child();
let turn_two = session_ctx.child();
```

Each turn context can read the session’s resources and the agent’s resources, but can only mutate its own.

## The SystemContext struct

The internal layout of `SystemContext` reflects the hierarchy design:

```text
SystemContext<'parent>
├── parent:    Option<&'parent SystemContext>   // read-only ancestor chain
├── globals:   Option<Arc<Resources>>           // server-level globals
├── resources: Resources                        // owned local state
└── outputs:   Outputs                          // owned ephemeral outputs
```

The `'parent` lifetime ties a child to its parent, enforced by the borrow checker. A root context created by `Server::create_context()` has `'static` lifetime because the `Arc<Resources>` keeps globals alive independently. The `globals` reference is cloned into every child context, so any context in the tree can reach server-level resources regardless of depth.

Outputs do not resolve through the parent resource hierarchy. They are owned by each context; clear stale outputs explicitly when starting a new independent turn. Successful execution moves the last output into `ExecutionResult`, rather than clearing every typed slot. See [Resources](/docs/polaris/core-concepts/resources.md) for how `Out<T>` works.
