Skip to content

RFC: Managed child-run lifecycle for repl_env #1478

Open

Description

@jucamohedano

Hi!,

I’ve been exploring repl_env and would like to propose extending its child-run lifecycle controls so a parent can start a child, continue other work, and manage that child across REPL steps. I’m raising this for design feedback before starting implementation.

Problem Statement

repl_env supports recursive child runs through rlm_query(...). A call runs the child and returns its answer to the parent. LocalRLMRunner also has start/complete callbacks that report lifecycle events, duration, and errors.

The parent currently waits for rlm_query(...) to return before its REPL code continues. Managed child handles would allow it to start a child, continue independent work, inspect the child's status, and collect its result later. Follow-up instructions and cancellation could extend that control.

Relevant current implementation and documentation:

Proposed Solution

Explore an environment-specific, handle-based lifecycle for child runs. The sketch below is illustrative only; names and sync/async semantics are open for discussion:

child = rlm_spawn("Analyze these documents")
summary = summarize_other_data()  # parent continues while child runs
status = child.status()
answer = child.result()
# Potentially: child.send("Focus on dates") or child.cancel()

The proposed scope is child lifecycle management within repl_env. Feedback would help define the smallest useful set of operations and their behavior across REPL steps and parent-session cleanup.

Alignment with Principles

This is an exploratory proposal; alignment should be reviewed with maintainers.

Principles Addressed

  • Minimize lifecycle deltas (training = production)
  • Minimize human-agent divergence
  • Be hands-on (working implementations)
  • Design for LLMs

No direct improvement against these principles is established yet. Adding child lifecycle controls does not itself reduce differences between training, evaluation, and production or between human and agent interfaces. A working implementation would follow design agreement. Alignment with “Design for LLMs” needs discussion: would explicit child controls improve model behavior enough to justify the additional API concepts and status messages compared with existing synchronous calls and batching?

Trade-offs

Option Pros Cons
Keep synchronous rlm_query(...) only Simple lifecycle and cleanup; existing callbacks already report completion and errors Parent must wait for each child result before continuing
Add managed child handles in repl_env Allows parent work to overlap with child execution; status/result and possibly follow-up/cancel operations Adds lifecycle, resource, concurrency, timeout, and error-handling complexity
Use existing batched recursive queries Runs several children concurrently through the current API Parent still waits for the batch before its REPL code continues; individual child controls are unavailable

Impact

Files/Systems Affected

  • Potentially envs/repl_env runner/backend, REPL namespace, tests, and documentation.
  • The proposal targets repl_env helpers and child lifecycle ownership within a parent session.

Related RFCs

  • None identified so far.

Implementation Sketch

A first prototype could focus on spawning children, inspecting status, and collecting results across REPL steps:

  1. Session ownership: Add a child-run manager alongside the recursive backend. The parent session owns the manager and its child registry, so handles remain usable across REPL steps. Each child gets a stable ID and a record containing its state, result, and error.

  2. Background execution: Expose rlm_spawn(...) in the REPL namespace. It schedules a child using the existing LocalRLMRunner and returns a handle promptly. Parent and child execution must keep their Python namespaces and captured output separate. The execution strategy should be agreed before implementation.

  3. Status and results: Provide a nonblocking status operation with explicit states such as queued, running, completed, and failed. Result collection returns the answer once available, reports when it is not ready, and surfaces a child's failure. Any waiting operation should have an explicit timeout. Existing lifecycle callbacks can continue providing notifications alongside these controls.

  4. Limits and cleanup: Apply recursion-depth, child-count, concurrency, and timeout limits when scheduling children. Define how outstanding work is stopped or awaited on parent finalization, reset, and close, and invalidate handles from the previous session. Existing child timeouts are cooperative and cannot interrupt an in-flight model call; cancelling a background task must not be treated as proof that its underlying execution has stopped. The initial prototype needs a clear cleanup contract even if user-facing cancellation and follow-up messages are deferred.

  5. Verification without model compute: Use a scripted chat_fn with controlled start/finish events and the real REPL/runner. Verify that a parent step can complete while a child is still running, a later step can retrieve the result through the same handle, child failures are surfaced, limits are enforced, and session cleanup leaves no child work running. Keep regression coverage for existing synchronous queries and batching.

The first reviewable result would be a small prototype demonstrating this trajectory: spawn a child in one REPL step, complete independent parent work in another, then collect the child's answer in a later step.

Open Questions

  1. How should child handles persist across REPL steps, and what should happen to running children when the parent session ends or resets?
  2. What concurrency, timeout, recursion-depth, and resource limits should apply?
  3. Should follow-up messages and cancellation be part of an initial scope, or should the first version only support spawn, status, wait, and result collection?
  4. What sync/async interface is appropriate for the persistent Python REPL?

I’d be interested in implementing and testing a scoped prototype after agreement on the lifecycle behavior. I’d appreciate feedback on the approach and initial scope.

cc @burtenshaw @sergiopaniego @kashif

Activity

  1. burtenshaw commented on Oct 26, 2026

    @burtenshaw
    Collaborator

    For v1, can you specify how reset/close guarantees child execution has stopped when a model call ignores cancellation?

  2. jucamohedano commented on Oct 26, 2026

    @jucamohedano
    Author

    One approach for v1 could be for the environment controller to supervise a separate worker process for each managed child. Each worker would construct its model client and execute the child runner and model-client calls inside that process. On reset/close, we’d request cooperative shutdown, then escalate to SIGTERM and SIGKILL after configurable timeouts. The controller would track every managed child worker, including nested child workers, and confirm each process has exited using OS process-exit waits before reporting successful cleanup.

  3. burtenshaw commented on Oct 27, 2026

    @burtenshaw
    Collaborator

    Sounds good. Do you want to open a PR?

  4. jucamohedano commented on Oct 28, 2026

    @jucamohedano
    Author

    Yes, I will follow with a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions