Repository navigation
Description
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_envrunner/backend, REPL namespace, tests, and documentation. - The proposal targets
repl_envhelpers 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:
-
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.
-
Background execution: Expose
rlm_spawn(...)in the REPL namespace. It schedules a child using the existingLocalRLMRunnerand 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. -
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.
-
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.
-
Verification without model compute: Use a scripted
chat_fnwith 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
- How should child handles persist across REPL steps, and what should happen to running children when the parent session ends or resets?
- What concurrency, timeout, recursion-depth, and resource limits should apply?
- 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?
- 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.
Activity
burtenshaw commentedon Oct 26, 2026
For v1, can you specify how reset/close guarantees child execution has stopped when a model call ignores cancellation?
jucamohedano commentedon Oct 26, 2026
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.
burtenshaw commentedon Oct 27, 2026
Sounds good. Do you want to open a PR?
jucamohedano commentedon Oct 28, 2026
Yes, I will follow with a PR.