Redeo Docs
DocsLADR / Stateful Sessions

Platform

Stateful Sessions

Stateful Sessions

A stateful session is a sequence of ladder invocations that share a slate. Open a session, run a ladder, the slate persists; run the ladder again with the same session id, the new call sees what was written last call. Sessions are how a ladder becomes a long-running reasoning agent rather than a one-shot program.

Why sessions exist

A ladder without sessions is stateless at the API level. Every call is independent; the ladder has no memory of past calls. But slates are declared in the config — they exist between calls — so where does the per-user state live?

The answer is: in a session. A session is the container that holds a slate's state across multiple API calls. Without a session, every call gets a fresh slate (or no slate at all). With a session, the slate accumulates across calls.

Why this matters.

  • Memory across calls. A reflexion ladder that writes lessons on call 1 should see them on call 2. Without sessions, each call's lessons vanish.
  • Conversational continuity. A research ladder that gathers context over multiple turns needs to remember what was already retrieved. Without sessions, every turn starts from scratch.
  • User-specific state. Different users invoking the same ladder should see different slates. Without sessions, the slate is either global (wrong) or absent (useless).

Sessions are the API-level mechanism that makes slates useful. Slates declare the schema; sessions provide the per-user state that fills it.

The session API

A session is created via the platform API. The session has a unique id; subsequent calls reference it.

bash
# Create a session for a ladder
POST /v1/{author}/{ladder}/sessions
{
  "name": "My research session"
}

# Response
{
  "id": "sess_abc123...",
  "ladder": "@alice/researcher",
  "createdAt": "2026-07-21T10:00:00Z"
}

Subsequent ladder invocations reference the session id:

bash
POST /v1/{author}/{ladder}/chat/completions
X-Session-Id: sess_abc123...
{
  "model": "gpt-4o",
  "messages": [{"role": "user", "content": "Research quantum error correction."}]
}

The ladder runs with the session's slate state loaded. Any writes during this call persist to the session's slate; the next call with the same session id sees them.

Without a session id, the ladder runs with an empty (or default) slate. Writes during the call may or may not persist depending on the platform's default behavior; assume they do not.

How sessions and slates relate

A slate is a schema declared in the ladder config. It defines what folders, files, and metatags exist.

A session is an instance of that schema with actual state. Multiple sessions can use the same ladder (and thus the same slate schema) but each has independent state. See Slates for the schema declaration reference.

text
Ladder "@alice/researcher" declares:
  slates:
    - title: Research Memory
      folders: [lessons, retrieved, notes]

Session sess_abc123 has its own instance:
  Research Memory / lessons / failures.md   <- contents from this session's calls
  Research Memory / retrieved / *            <- files from this session's retrievals

Session sess_def456 has a different instance:
  Research Memory / lessons / failures.md   <- different contents
  Research Memory / retrieved / *            <- different files

The schema is shared; the state is per-session. This is how thousands of users can invoke the same published ladder without their lessons colliding.

Per-session slate state is isolated. Session A cannot read session B's slate. Cross-session sharing is via explicit cross-ladder jumps (where one ladder's output becomes another's input); sharing via published slates is planned.

Session lifecycle

A session has a lifecycle.

Created. A new session starts with empty slates. Every declared file in the config's slates: is initialized per its init: field (blank or literal content). The slate is fresh.

Active. Each call with the session id loads the current slate state, runs the ladder, persists any writes. The slate grows over time as the ladder writes to it.

Paused (optional). A session can be paused mid-execution via the debug API. The current execution state is preserved; the session can be resumed from where it left off.

Closed. A session can be explicitly closed. After closing, calls with that session id fail. The slate state may be retained for a configurable period (for audit) or deleted immediately, depending on platform policy.

Expired. Sessions that have not been used for a configurable period (default 30 days, configurable per platform) auto-expire. The slate state may be deleted or archived depending on policy.

The caller controls session creation and closure. The platform controls expiry and retention. Authors do not interact with sessions directly; they just declare slates and let the session system provide the state.

Concurrency and consistency

Sessions guarantee single-writer per call. Within one execution, only one ladder instance writes to the session's slate. No race conditions.

Across executions, the platform enforces last-write-wins consistency. If two calls with the same session id somehow run concurrently (rare; usually the caller serializes them), the second call's writes overwrite the first's. A planned update adds vector clocks and a user-visible diff tool for finer-grained conflict resolution.

Recommended pattern. Callers should serialize calls to the same session. Most natural usage is conversational: the user sends a message, the ladder responds, the user sends another message. The session accumulates state across the conversation. Concurrent calls to one session are unusual and not well-supported.

Cross-user isolation. Each session belongs to one user. Different users invoking the same ladder get different sessions automatically. There is no shared state across users (optional shared slates for collaborative scenarios are planned).

Common patterns

Conversational agent. A chat-style ladder that remembers the conversation.

yaml
name: Research Assistant
allowedTargets: { strategy: universal }
exit: answer
knobs: {}
slates:
  - title: Conversation Memory
    folders:
      - name: history
        tokenLimit: 5000
        access: readWrite
        evictionPolicy: FIFO
        files:
          - { name: messages.md, init: blank }
      - name: context
        tokenLimit: 2000
        access: readWrite
        files:
          - { name: facts.md, init: blank }

steps:
  - id: answer
    fields:
      - { name: Question,  type: text,      from: input.context }
      - { name: History,   type: slateRead, from: { slate: "Conversation Memory", folder: history, file: messages.md } }
      - { name: Facts,     type: slateRead, from: { slate: "Conversation Memory", folder: context, file: facts.md } }
    systemPrompt: "Answer the question, using History and Facts as context."
    slateWrite:
      to: { slate: "Conversation Memory", folder: history, file: messages.md }
      on: append
      match: { type: object, required: [exchange], properties: { exchange: { type: string } } }

Each call: read prior history and facts, answer, append this exchange to history. Across calls in a session, the conversation accumulates.

Long-running research agent. A research ladder that gathers, synthesizes, and persists findings across days.

bash
# Day 1: open a session, gather initial findings
POST /v1/{author}/researcher/sessions
{ "name": "Quantum computing deep dive" }
# -> sess_abc123

POST /v1/{author}/researcher/chat/completions
X-Session-Id: sess_abc123
{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Start with quantum gates."}] }

# Day 2: continue the same session
POST /v1/{author}/researcher/chat/completions
X-Session-Id: sess_abc123
{ "model": "gpt-4o", "messages": [{"role": "user", "content": "Now dive into error correction."}] }

# The ladder sees day 1's findings in its slate.

The slate accumulates across days. The ladder builds on prior research rather than restarting each call.

Reflexion across calls. A reflexion ladder that learns from its mistakes permanently.

yaml
slates:
  - title: Reflexion Memory
    folders:
      - name: lessons
        tokenLimit: 1000
        access: readWrite
        evictionPolicy: LRU        # keep the most-recently-referenced lessons
        files:
          - { name: failures.md, init: blank }

On every failed verification, the ladder writes a lesson. On every subsequent call (within the session), the generate step reads the lessons file and avoids past mistakes. The ladder gets better over time within a session.

Multi-ladder session (planned). A session that multiple ladders contribute to. One ladder writes context; another reads it. The session becomes a shared workspace for a family of related ladders. Currently each session is bound to one ladder; lifting this is planned.

Common pitfalls

Forgetting to pass the session id. Calls without a session id do not see prior slate state. The caller must explicitly pass X-Session-Id on every call that should see prior state. A common bug: forget the header on one call, that call sees empty slates.

Expecting sessions to share state across ladders. Each session is currently bound to one ladder. Two different ladders cannot share a session. If you need cross-ladder state, use cross-ladder jumps (the target ladder runs in the same session).

Letting sessions grow unbounded. A session that's been used for months can accumulate huge slate state. Token costs grow with slate size. Use eviction policies (FIFO, LRU) and tight tokenLimits to bound growth.

Expecting concurrency safety across calls. Single-writer-per-call is guaranteed; single-writer-across-calls requires the caller to serialize. Don't fire concurrent calls to the same session and expect consistent state.

Forgetting that local ladders don't have sessions. Local ladders (in the local/ namespace) run in the local runtime. They have no platform session API. Their slate state persists in local files between calls, but the platform session lifecycle does not apply.

Using sessions when a single call would do. Sessions add overhead (slate load, state management). If your use case is one-shot, don't open a session. Sessions are for sequences of related calls.