Repository navigation
fix(env): one grant-reach check, size-scaled load timeouts on container sandboxes, and JSON state for services that export a file - #65
Conversation
Every caller that hands a transfer grant on checked both supports_transfer_grants and grants_reach(sandbox_type), and a caller that checked only one half would pass most tests. issues_grants_to answers both, and choose_transfer, _require_object_form, transfer_store, readable_url and _snapshot_upload use it. _require_object_form keeps its two error messages. No behaviour change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
_staged_artifact_size ran `docker exec ... stat` through exec_script, which only VM sandboxes have. On a container sandbox (Modal) the stat raised, so every load there fell back to the floor timeout however large its payload. A container sandbox is the env's own container, so stat the file in it directly; the VM path is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…with a file snapshot_agent_state and verify_universe_roundtrip took a v1 service's state from data/get's first part as a DataPart. A service that exports a file bundle of its database answers with a FilePart instead, so snapshot_agent_state skipped it with a warning and verify_universe_roundtrip failed. legacy_protocol.service_state gives both the same answer: the data data/get returns when it is data, else the JSON the service serves at /export-state, at the address the env card gives it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A VM-backed sandbox in container mode (the local one) runs exec on the container's Docker host, not inside the container, so stat there missed the staged file and the load fell back to the floor. Reach the container the way write_file_from_s3 does, by name. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…-reach-and-load-fixes
…e reads as JSON from a file export The integration tiers ran the staged-payload stat on every local server-env load but never checked it, and no test read a service whose data/get answers with a file. The items server now serves its state at /export-state and, with urn:agentenv:export-as-file/v1 enabled, answers data/get with a file bundle of it. On the local backend (CI) and on Modal (when the resolved config reaches it), a server env's load measures the payload it staged through the deploy's own handle and a restored one, and service_state reads the state as JSON in both modes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| return {} | ||
| if isinstance(response.parts[0], DataPart): | ||
| return response.parts[0].data | ||
| return await _export_state_at(base_url, timeout=60, verify=True) |
There was a problem hiding this comment.
The new file-export fallback hardcodes timeout=60, while export_state has a separate 60-second default. The repository requires magic numbers to have descriptive names. Use a named timeout so these paths cannot drift when someone changes one of them. This requirement must be met before merging.
Rule Used: Store magic numbers as class or instance variables with descriptive names rather than using them inline in the code. (source)
Learned From
scaleapi/scaleapi#126388
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agent_env/env/legacy_protocol.py
Line: 109
Comment:
**Export timeouts can drift**
The new file-export fallback hardcodes `timeout=60`, while `export_state` has a separate 60-second default. The repository requires magic numbers to have descriptive names. Use a named timeout so these paths cannot drift when someone changes one of them. This requirement must be met before merging.
**Rule Used:** Store magic numbers as class or instance variables with descriptive names rather than using them inline in the code. ([source](https://app.greptile.com/scale-ai/-/custom-context?memory=002e0051-41ad-46c1-9098-47433c580150))
**Learned From**
[scaleapi/scaleapi#126388](https://github.com/scaleapi/scaleapi/pull/126388)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Named in f2bf992: EXPORT_STATE_TIMEOUT_SECONDS, the default of export_state and the timeout of the file-export fallback.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
End-to-end and chaos results (head 7e40368, with main merged)Setup:
The grant-reach refactor: re-running the file-delivery matrix from #61 on this head gave the same results as on #61. That covers grants, signed URL, staged on the agent, and a user-sim that can be given no URL. The load rows were rerun on 7e40368, after the timeout moved to the store's metadata; the other rows ran on 3046e9a. The capture path didn't change between the two. Teardown: every sandbox terminated, 0 failed. |
…s payload, not a stat in the sandbox The staged file is a byte-for-byte copy of the artifact's object, so the store already knows its size. Asking it, as load_by_signed_url does for plugin-deployed envs, replaces the stat that had to know how to reach the env's container on each kind of sandbox (in it on Modal, through docker on a VM-backed sandbox in container mode, through the gateway's container on a VM) and that a new sandbox backend could break again. A store that can't say still leaves the floor timeout. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Summary
Three small core fixes, one commit each.
issues_grants_to(store, sandbox_type)replaces thesupports_transfer_grants and grants_reach(...)pair at all five call sites, so a new caller can't check only one half. No behaviour change._require_object_formkeeps its two error messages._staged_artifact_sizeranstatthroughexec_script, which only VM sandboxes have. On a container sandbox (Modal) it raised, so every load there got the floor timeout whatever the payload's size. It now takes the size from the store's metadata for the artifact's object (get_object_metadata_at, which every store implements), asload_by_signed_urlalready does for plugin-deployed envs. The staged file is a byte-for-byte copy of that object. That removes the per-sandbox-kindstat(in the container on Modal, through docker on a VM-backed container sandbox, through the gateway's container on a VM), so a new sandbox backend can't break it again. A store that can't say still leaves the floor.snapshot_agent_stateandverify_universe_roundtripassumed a v1 service'sdata/getanswer starts with aDataPart. A service that exports a bundle of its database answers with aFilePart, sosnapshot_agent_stateskipped it with a warning andverify_universe_roundtripfailed. The newlegacy_protocol.service_statereturns the data when the answer is data, and otherwise reads the service's/export-stateat the address its env card gives.Testing
tst/unit+ protocol, 6330 passed. New tests cover:service_statefor a data answer, a file-bundle answer and an empty answer;snapshot_agent_statecapturing a bundle-exporting service from/export-state./export-state, and withurn:agentenv:export-as-file/v1enabled it answersdata/getwith a file bundle.server_env_local_test(CI): a local server env's load measures the payload it staged, through the deploy's own handle and through a restored one.service_statereads the server's state as JSON in both answer modes.server_env_modal_test: the same on Modal, when the resolved config reaches it. CI skips it.service_statewithout its file fallback:'FilePart' object has no attribute 'data';[None, None] == [31, 31].End-to-end and chaos results are in a comment below.
🤖 Generated with Claude Code