> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qredence.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Recursive RLM delegation

> How fleet-rlm runs a Turn as one fresh native dspy.RLM, walks the cheapest-sufficient ladder, and isolates a single native child level under bounded budgets.

Every user Turn in fleet-rlm runs as exactly one fresh native `dspy.RLM`. The Root RLM executes inside a Daytona Sandbox whose Python interpreter context persists across RLM iterations within a Run. Each new Run starts with a fresh interpreter context, so nothing leaks between Runs.

This page describes how the Root Turn decides what to do next, when it delegates to a native child RLM, and the fixed one-level recursion boundary that keeps the tree shallow and auditable.

## One Turn, one Root RLM

A Turn maps to a single native `dspy.RLM` invocation. The Root RLM works iteratively inside its Sandbox: it writes Python, inspects results, and refines its plan until it emits `SUBMIT`. Interpreter state is reused across those iterations so variables from an earlier step remain available in the next one. Replacing a Sandbox mid-Run remounts the Workspace Volume Scope but does not preserve Python globals, so long-lived REPL state only survives while the same Sandbox does.

`RLMRunner` in `src/fleet_rlm/rlm/runner.py` builds this single fresh RLM per Turn and wires its instruction fragments from `src/fleet_rlm/rlm/instructions.py`.

## The cheapest-sufficient ladder

Inside the Root RLM, you pick the cheapest option that still solves the sub-problem. Only escalate when the previous rung is insufficient.

```mermaid theme={null}
flowchart TD
    Root["Root RLM (one Turn)"] --> Py["1. Python in the interpreter<br/>deterministic, in-process"]
    Py --> LLM["2. Native llm_query / llm_query_batched<br/>semantic work, no new RLM"]
    LLM --> One["3. rlm_query(prompt=...)<br/>one isolated child RLM"]
    One --> Many["4. rlm_query_batched (Root only)<br/>ordered independent child RLMs"]
    Many --> Verify["Root verifies and synthesizes<br/>child evidence"]
    Verify --> Submit["SUBMIT"]
```

1. **Python.** Prefer deterministic work directly in the interpreter context.
2. **Native `llm_query` / `llm_query_batched`.** Use these when you need a language model, but not a new recursive agent. No child RLM is spawned.
3. **`rlm_query(prompt=prompt)`.** Delegate one iterative isolated subproblem to a native child harness. Use this when the subproblem needs its own tool-using agent loop.
4. **Root-only `rlm_query_batched`.** Fan out ordered, independent child RLMs when you can decompose a task into siblings that do not depend on each other. Only the Root may open this rung. After siblings return, the Root verifies their evidence and synthesizes an answer before `SUBMIT`.

Children return bounded evidence, not final answers. Final synthesis is always the Root's job.

## One native child level, no deeper

The recursive child boundary is a fixed product invariant, not a knob:

```
RLM_NATIVE_CHILD_DEPTH = 1
```

Only the Root may open a native child RLM, and that child may not open another. Policies that still set `rlm.recursion_max_depth` fail startup validation. This keeps traces shallow, budgets predictable, and cleanup deterministic.

Dispatch and bounds for the single child rung live in `src/fleet_rlm/rlm/recursive_calls.py`, ordered sibling fan-out lives in `src/fleet_rlm/rlm/recursive_batch.py`, and the child Sandbox lifecycle lives in `src/fleet_rlm/daytona/recursive_child_runtime.py`.

## Child isolation under `daytona-recursive`

Under the `daytona-recursive` profile, each child RLM runs in its own dedicated Daytona Sandbox with strict boundaries:

* Fresh, dedicated Daytona Sandbox per child, distinct from the Root Sandbox.
* Ordinary Daytona network egress, the same as any Sandbox.
* The same Volume ID mounted at `recursive/<workspace-id>/<run-id>/<call-index>`. This private sibling scope cannot reach the Root `workspaces/<workspace-id>` mount, so a child cannot read or overwrite Root workspace state.
* No Fleet Tools and no credentials are exposed to the child.
* Strict cleanup: the child's scope is purged and its Sandbox is deleted before Root success can commit. If cleanup fails, the Root Turn does not report success.

The default `daytona` profile keeps recursion disabled. Enable one child level by switching to the `daytona-recursive` profile.

## Recursion bounds

All recursion bounds live in the `[rlm]` section of `config/fleet.toml`. Fleet reserves the shared recursive budget atomically before starting a child, so parallel children cannot double-count against the same allowance.

| Key                                | Purpose                                                                                                 |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `recursion_enabled`                | Enable one native child level. Default `daytona` profile keeps it off; `daytona-recursive` turns it on. |
| `recursion_max_calls`              | Maximum number of native child queries the Root may issue per Turn.                                     |
| `recursion_max_prompt_chars`       | Cap on the prompt characters passed to each child.                                                      |
| `recursion_child_max_iters`        | Iteration cap inside each child RLM.                                                                    |
| `recursion_child_max_llm_calls`    | Semantic LM call cap inside each child.                                                                 |
| `recursion_child_max_output_chars` | Output character cap returned from each child.                                                          |
| `recursion_max_parallel_children`  | Maximum concurrent independent child RLMs. Defaults to `2`.                                             |

See the [configuration reference](/fleet-rlm/reference/configuration) for the surrounding `[rlm]` keys and profile wiring.

## Root synthesis, not child answers

Batched siblings return evidence: extracted facts, structured findings, or scoped conclusions. The Root then verifies that evidence against the original goal and synthesizes the final response before emitting `SUBMIT`. This keeps children small, replaceable, and auditable, and it keeps final answers grounded in Root-side reasoning that has full Turn context.

The verification and bounded-context expectations are encoded as instruction fragments in `src/fleet_rlm/rlm/instructions.py` alongside the base, REPL, tool, and optional recursion fragments.

## REPL variable mode and large inputs

Native `dspy.RLM` in DSPy `>= 3.3.0` uses the `SandboxSerializable` contract to hold large inputs as REPL variables inside the child's persistent Python interpreter context. Those variables are not injected into the model prompt; the RLM works with them programmatically, only surfacing the fragments it needs.

This is upstream DSPy behavior, so fleet-rlm does not maintain wrapper code for large-input handling. You benefit from it automatically when you pass large context objects that implement the contract.

## Interpreter reuse within a Run

Within one Run, interpreter calls reuse a single context so Python state persists across RLM iterations in the Root Sandbox. Child RLMs each get their own interpreter context in their own Sandbox, and that context is discarded when the child is deleted. Every later Run receives a fresh interpreter context in a fresh Root Sandbox.

Daytona runtime wiring for both Root and recursive-child paths lives in `src/fleet_rlm/composition/daytona.py`.

## Implementation pointers

| File                                               | Role                                                                                        |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `src/fleet_rlm/rlm/runner.py`                      | `RLMRunner` executes one fresh DSPy RLM per Turn.                                           |
| `src/fleet_rlm/rlm/recursive_calls.py`             | Recursive child dispatch and bounds.                                                        |
| `src/fleet_rlm/rlm/recursive_batch.py`             | Ordered sibling fan-out.                                                                    |
| `src/fleet_rlm/rlm/instructions.py`                | Instruction fragments: base, REPL, tool, optional recursion, verification, bounded context. |
| `src/fleet_rlm/daytona/recursive_child_runtime.py` | Dedicated child Sandbox lifecycle.                                                          |
| `src/fleet_rlm/composition/daytona.py`             | Daytona runtime wiring for Root and child.                                                  |

## See also

<CardGroup cols={2}>
  <Card title="Daytona runtime" href="/fleet-rlm/concepts/daytona-runtime">
    Sandbox lifecycle, volumes, and how the Root and recursive-child paths are wired.
  </Card>

  <Card title="Agent model" href="/fleet-rlm/concepts/agent-model">
    How a Turn maps to one fresh RLM and where the ladder sits in the runtime.
  </Card>

  <Card title="Configuration" href="/fleet-rlm/reference/configuration">
    The full `[rlm]` section and profile wiring for `daytona` and `daytona-recursive`.
  </Card>

  <Card title="HTTP API" href="/fleet-rlm/reference/http-api">
    Turn and Run endpoints that drive the Root RLM and observe recursion bounds.
  </Card>
</CardGroup>
