Repository navigation
feat(multi-env): child_envs on deploy_env, each child env's options by its name (FD-4930) - #522
Open
earakely-scale wants to merge 5 commits into
Open
earakely-scale wants to merge 5 commits into
earakely-scale wants to merge 5 commits into
Conversation
…y its name (FD-4930)
A deploy_env step on a multi env takes child_envs: each child env's options by its
environment name, as a step deploying that env alone would give them (its sandbox
fields and its env_params). The multi env checks them at save and at deploy: an
unknown name, an entry with other keys, and an entry for an MCP server or a website,
which run on the multi env's own sandbox, are refused, each problem under
child_envs.<name>. An env with no child envs refuses child_envs.
A bundle export drops each entry's sandbox_type as it drops the step's own, through a
field path that reaches every value of a table (child_envs{}.sandbox_type).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a run does to_dict() handed out the step's own child_envs, which a bundle export prunes in place, so exporting stripped each child env's sandbox_type from the loaded step. It now returns a copy. preflight() asked the env's provider about child_envs, and a provider that takes every option passed them, while execute() refused them for an env whose deploy() takes none. preflight() now checks the env's deploy() as execute() does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…kes none, as a run does preflight() asked the env's provider about env_params, and a provider that takes every option passed them, while execute() refused them for an env whose deploy() takes none: a task saved and then failed at its run. preflight() now checks the env's deploy() for env_params as it does for child_envs, with the words execute() raises. execute() asks _takes() for every optional keyword it forwards, in place of the inline signature checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a real multi env holding an MCP server, behind the local gateway: each entry the multi env refuses (an MCP server's, an unknown name, another key) and env_params on the multi env are refused at save and at the run with the same words, and the local provider makes no sandbox for any of them; without them the same env deploys and serves its server's list_items through the gateway's /mcp. child_envs_refusal names the other keys of an entry as strings, so an entry whose keys are of two types is refused rather than raising. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment on lines
+70
to
+72
| deployed_env = await Env.from_instance_id(deployed.instance_id) | ||
| work_dir = deployed_env._sandbox.work_dir | ||
| try: |
There was a problem hiding this comment.
Failed reconnect leaves containers running
If Env.from_instance_id() raises after deployment, the test never enters its cleanup block. The local_stack fixture only removes containers matching agent-env-local-, so this Docker Compose stack stays running and keeps using memory and host ports. Put reconnecting inside a cleanup block that can tear down the deployed sandbox even when reconnecting fails.
Prompt To Fix With AI
This is a comment left during a code review.
Path: tst/integration/env/envs/multi_env_child_envs_local_test.py
Line: 70-72
Comment:
**Failed reconnect leaves containers running**
If `Env.from_instance_id()` raises after deployment, the test never enters its cleanup block. The `local_stack` fixture only removes containers matching `agent-env-local-`, so this Docker Compose stack stays running and keeps using memory and host ports. Put reconnecting inside a cleanup block that can tear down the deployed sandbox even when reconnecting fails.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.…elled call's test never reads it empty test_a_cancelled_call_stops_the_command_it_was_running waits for the fake's pid file to exist, then reads the pid. The fake wrote it with write_text, which creates the file empty and then writes it, so on a busy runner the test could read '' (it failed #522's unit shard with "invalid literal for int() with base 10: ''"). The fake now writes pid.tmp and renames it into place, so the file exists only with the pid in it. With the CPU saturated, polling the file as the test does read it empty 16 times in 600 with the old write and 0 in 600 with the new one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR 1 of the four in the FD-4930 + FD-4935 design note, settled Nov 6. A child env of a multi env takes the same settings it takes deployed alone, so a
deploy_envstep on a multi env takeschild_envs: each child env's options by its environment name, as a step deploying that env alone would give them.{"id": "office", "type": "deploy_env", "env_id": "office", "sandbox_type": "modal", "child_envs": {"desktop": {"sandbox_type": "modal", "cpu": 4, "memory_mb": 8192, "env_params": {"form": "container", "site_routing": true}}}}No child env takes an entry until computers become child envs (FD-4935, PR 3), so this PR is the field and its checks:
DeployEnvTaskStep.child_envs, written only when set.preflightandexecutepass it to the env, andexecuterefuses it for an env whosedeploy()doesn't take it.MultiEnv.child_envs_refusal, run at save and again at deploy, before anything is deployed. It refuses:sandbox_type,cpu,memory_mb,disk_size_gbandenv_params(the multi env's own step sets the lifetime, gateway and state for every child env);Each problem is reported under
child_envs.<name>.deploy()takes nochild_envsrefuses them at save and at run with the same words,mcp_server envs have no child envs, whatever its provider takes.to_dict()returns a copy of them, so a bundle export, which prunes in place, leaves the loaded step as it was.env_paramshad the same gap: behind a provider that takes every option, a task withenv_paramsfor an env whosedeploy()takes none was accepted at save and refused at its run.preflightnow checks the env'sdeploy()forenv_paramstoo, and refuses with the words the run raises (mcp_server envs take no env_params).executeasks one helper,_takes, for every optional keyword it forwards.sandbox_type, the user's provider, as it drops the step's own. Field paths now take{}for every value of a table, andchild_envs{}.sandbox_typeis user-scoped.Also a flaky test this PR's CI hit, fixed here:
macos_harness_driver_test.py::test_a_cancelled_call_stops_the_command_it_was_runningwaits for its fake's pid file to exist and then reads the pid, but the fake wrote it withwrite_text, which creates the file empty first, so a busy runner could read''. The fake now writespid.tmpand renames it into place. Polling the file as the test does, with the CPU saturated, read it empty 16 times in 600 with the old write and 0 in 600 with the new.Test plan
deploy_env_test.py: the round trip; the export dropping each entry'ssandbox_type; the refusal for each kind of env with no child envs; each multi env refusal, naming the child env;MultiEnv.deployrefusing before it deploys anything;executepassingchild_envsto a deploy that takes them and refusing them for one that doesn't.tst/unit/task_step,env,bundle,serializationandtaskon macOS: 5,616 passed. The failures are the twotest_disk_recipetests that fail on main on macOS too, and three modules the local venv can't import (hypothesis,openenv).tst/integration/env/envs/multi_env_child_envs_local_test.py(slow tier), on a real multi env holding an MCP server on the local backend: each entry the multi env refuses (an MCP server's, an unknown name, another key) andenv_paramson the multi env are refused at save and at the run with the same words, and the local provider makes no sandbox for any of them (a spy on its create calls). Without them, the same env deploys and serves its server'slist_itemsthrough the gateway's/mcp.multi_env_local_test.pypassed too. Nothing was left running.env_paramson the multi env are each refused at save, with no container created. The plain task passes.child_envsandenv_paramsvalues (non-tables, unknown, empty, long and non-ASCII names, keys of every type, NaN) on a stored multi env. The 341 that set something were refused at save and at the run with the same words, with no other exception and no sandbox made; the 59 that set nothing were accepted. It found that an entry with keys of two types raisedTypeErrorinstead of being refused; fixed in ba6f25d, with a unit test.kill -9in the middle of a deploy, then a rerun: the rerun passes. The killed run's compose stack is left, as any SIGKILLed run's is. Ctrl-C in the middle of a deploy: the run cancels and tears down; the rerun passes.stress-ng(8 CPU, 4 IO, 2 memory workers): passed in 96 s.agent-env runon a bundle whose multi env holds the echo MCP server. The VM is torn down.child_envs: the multi env deploys in 32 s, the echo verifier passes, and the run tears down its sandbox.child_envs.echo: echo runs on the multi env's own sandbox, which the step's own fields set, so it takes no entrychild_envs.desktop: env '@local/~/bundle/office' has no child env named 'desktop'; its child envs are echochild_envs.echo: an entry is a table of sandbox_type, cpu, memory_mb, disk_size_gb, env_params, not ttl_secondsenv_paramson the multi env) builds its env's image the same way, so that's existing behavior.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge, with one non-blocking cleanup issue remaining in the integration test.Fix with agent prompt
Summary
Adds
child_envsto deployment steps and checks each entry before deployment.env_paramsbefore they run.No new issues were found. The earlier export-copy and save-check findings are fixed.
Reviews (10) · Last reviewed commit: "test(drivers): the macos-harness fake wr..." · Reviewed by Greptile