Skip to content

fix(env): one grant-reach check, size-scaled load timeouts on container sandboxes, and JSON state for services that export a file - #65

Merged
earakely-scale merged 8 commits into
mainfrom
edgararakelyan/grants-reach-and-load-fixes
Oct 7, 2026
Merged

earakely-scale merged 8 commits into
mainfrom
edgararakelyan/grants-reach-and-load-fixes

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Three small core fixes, one commit each.

  • One check for "this store's grants reach that sandbox". issues_grants_to(store, sandbox_type) replaces the supports_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_form keeps its two error messages.
  • Load timeouts on container sandboxes. _staged_artifact_size ran stat through exec_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), as load_by_signed_url already does for plugin-deployed envs. The staged file is a byte-for-byte copy of that object. That removes the per-sandbox-kind stat (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.
  • JSON state for services that export a file. snapshot_agent_state and verify_universe_roundtrip assumed a v1 service's data/get answer starts with a DataPart. A service that exports a bundle of its database answers with a FilePart, so snapshot_agent_state skipped it with a warning and verify_universe_roundtrip failed. The new legacy_protocol.service_state returns the data when the answer is data, and otherwise reads the service's /export-state at the address its env card gives.

Testing

  • Unit: tst/unit + protocol, 6330 passed. New tests cover:
    • a load whose RPC timeout follows the size its store holds for the payload;
    • a store that can't size the payload, with no metadata or a failing lookup, leaving the floor;
    • service_state for a data answer, a file-bundle answer and an empty answer;
    • snapshot_agent_state capturing a bundle-exporting service from /export-state.
  • Integration: the items server fixture now serves /export-state, and with urn:agentenv:export-as-file/v1 enabled it answers data/get with 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_state reads 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.
  • Mutant checks, all failing their tests:
    • service_state without its file fallback: 'FilePart' object has no attribute 'data';
    • on Modal, main's staged-size code: [None, None] == [31, 31].
  • Plugin API: no break to the plugin surface.

End-to-end and chaos results are in a comment below.

🤖 Generated with Claude Code

earakely-scale and others added 3 commits October 6, 2026 09:48
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>
@earakely-scale
earakely-scale requested a review from a team as a code owner October 6, 2026 16:48
Comment thread src/agent_env/env/envs/mcp_server.py Outdated
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>
@earakely-scale earakely-scale changed the title Grant-reach helper, container load timeouts, and JSON state for services that export a file fix(env): one grant-reach check, size-scaled load timeouts on container sandboxes, and JSON state for services that export a file Oct 6, 2026
earakely-scale and others added 2 commits October 6, 2026 20:27
…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>
Comment thread src/agent_env/env/legacy_protocol.py Outdated
return {}
if isinstance(response.parts[0], DataPart):
return response.parts[0].data
return await _export_state_at(base_url, timeout=60, verify=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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)

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!

Fix in Cursor Fix in Claude Code Fix in Codex

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@earakely-scale

earakely-scale commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator Author

End-to-end and chaos results (head 7e40368, with main merged)

Setup:

  • Real env services on two remote sandbox providers (Modal containers, and a VM provider), against a dev deployment.
  • The services are production-shaped MCP servers whose data/get exports a file bundle of their database.
  • Each case was also run on main's code as a baseline.
case Modal VM provider main, Modal
3.4 GB bundle loaded into a single-service env: staged size measured, timeout scaled 3,369,336,460 B → 3600 s (floor 900 s); load ok same; load ok "could not stat staged payload (… no attribute 'exec_script'); using the floor timeout"
the store's metadata lookup forced to fail warning, floor timeout, load ok same –
a two-service env (calendar + chat, both answer data/get with a file): snapshot_agent_state capture and verify_universe_roundtrip export both services captured and exported as JSON, equal table by table to /export-state (687,286 messages, 53,422 files, 985 channels, …) same both services skipped ('FilePart' object has no attribute 'data'); the export raises

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>
@earakely-scale
earakely-scale merged commit 3982654 into main Oct 7, 2026
14 checks passed
@earakely-scale
earakely-scale deleted the edgararakelyan/grants-reach-and-load-fixes branch October 7, 2026 04:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant