Skip to content

fix(openenv): raise on a step before reset, and end an episode whose end_turn is refused in an observation - #350

Merged
earakely-scale merged 3 commits into
mainfrom
edgararakelyan/openenv-adapter-contract
Oct 27, 2026
Merged

earakely-scale merged 3 commits into
mainfrom
edgararakelyan/openenv-adapter-contract

Conversation

@earakely-scale

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

Copy link
Copy Markdown
Collaborator

What and why

OpenEnv's review of the agentenv importer (openenv/OpenEnv#1493) checked AgentEnvEnvironment against RFC 015's contract for imported envs, and found two gaps:

  • A step before reset. RFC 015 has an imported env raise on a tool call before reset(), so an agent can't start an episode. OpenEnv's ORS wrapper raises RuntimeError("Call reset() before invoking ORS tools"). AgentEnvEnvironment answered with an ended error observation instead.
    • It now raises RuntimeError, for either action, from step_async and from the worker-thread step. Over OpenEnv's WebSocket the client gets an error message, and the session goes on.
    • After a reset, nothing changes: once an episode has ended, or a reset was refused, a step still answers with the ended observation.
  • An end_turn the proxy refuses. Turn.end raises EpisodeError when the proxy no longer has the turn (a 404) or refuses the reply. _end_turn called it outside its error handler, so the error escaped step. It's inside the handler now, so the episode ends in an error observation and is torn down, like every other failure.

Teardown was already safe. Closing a turn's tools does nothing that can fail, and Episode.abandon waits for the run with asyncio.wait, which doesn't re-raise the run's error. A new test pins this down: a teardown that fails still answers with an observation and frees the session. The observation is unscored, because the run's result reports the teardown error.

Also, a flaky test. test_a_background_apps_output_is_drained_after_the_call_returns (protocol package, computer extension) failed in this PR's first CI run, on both the unit job and CodeBuild.

  • It slept 1 s and then expected the drain set to be empty. A loaded runner can take longer to start the background child and finish its sleep 0.4.
  • It now polls until the drain set is empty, failing after 10 s.
  • Nothing else in this PR touches that code.

How it was tested

  • pytest tst/unit/openenv tst/unit/test_optional_extras_stay_optional.py: 69 passed.
    • New tests: a step before reset (both actions, on the event loop and from a worker thread, and over OpenEnv's WebSocket in app_test), the refused end_turn, and the failing teardown.
    • Against the old adapter, the five tests that cover the change fail.
  • pytest tst/integration/openenv on local Docker (macOS): 6 passed. The seventh failed building the echo agent image for linux/amd64 on this arm64 Mac, before the adapter runs; CI runs it on Linux.
  • pytest packages/agentenv-protocol/tests -n 12, three times: 801 passed each time.
  • ruff check passes.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 4/5

The timing settings need descriptive names to satisfy the repository rule before merging.

Fix All in CursorFindings

  1. P2 Test timing settings lack names ▶
Fix with agent prompt
### Issue 1
packages/agentenv-protocol/tests/test_computer_extensions.py:170-172
The drain test uses an inline polling delay of `0.05` and timeout of `10`. The repository requires magic numbers to be stored as class or instance variables with descriptive names. Name these settings so readers can tell what each controls. This repository requirement must be satisfied before merging.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

The PR makes OpenEnv reject steps before the first reset and return an ended error observation when end_turn fails.

  • OpenEnv sessions now reject steps until their first reset.
  • Refused end-turn replies now end as error observations.
  • The computer-output drain test waits for background output to finish.

Reviews (3) · Last reviewed commit: "test(protocol): bound the drain wait wit..." · Reviewed by Greptile

…end_turn is refused in an observation

RFC 015 has an imported env raise on a tool call before reset(), so an agent
can't start an episode; AgentEnvEnvironment answered with an ended error
observation instead. A step before the session's first reset now raises
RuntimeError. Once a reset has happened, an ended episode still answers with
the ended observation.

Turn.end raises EpisodeError when the proxy no longer has the turn or refuses
the reply, and _end_turn called it outside its error handler. It's inside now,
so the episode ends in an error observation and is torn down.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…f a fixed second

The drain test slept 1s and then expected the drain set empty, but a loaded CI
runner can take longer to start and finish the background child's sleep 0.4.
It failed on both CI systems for an unrelated change. It now polls until the
drain set is empty, failing after 10s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread packages/agentenv-protocol/tests/test_computer_extensions.py Outdated
… has

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment on lines +170 to +172
await asyncio.sleep(0.05)

await asyncio.wait_for(drained(), timeout=10)

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 Test timing settings lack names

The drain test uses an inline polling delay of 0.05 and timeout of 10. The repository requires magic numbers to be stored as class or instance variables with descriptive names. Name these settings so readers can tell what each controls. This repository requirement must be satisfied 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: packages/agentenv-protocol/tests/test_computer_extensions.py
Line: 170-172

Comment:
**Test timing settings lack names**

The drain test uses an inline polling delay of `0.05` and timeout of `10`. The repository requires magic numbers to be stored as class or instance variables with descriptive names. Name these settings so readers can tell what each controls. This repository requirement must be satisfied 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.

Leaving these inline. This repo writes test timings inline, as this test already does two lines above (DRAIN_S patched to 0.1, the sleep 0.4) and as the package's other tests do (wait_for(..., timeout=1) in test_a2a_agent.py). The rule cited comes from another repository.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You’re right. This package uses inline timing values, and the 0.05 polling interval and 10-second bound are consistent with that convention. I applied a shared magic-number instruction as though it were a requirement for this repo. I’m withdrawing the finding; no change is needed.

@earakely-scale
earakely-scale merged commit 048cafa into main Oct 27, 2026
14 checks passed
@earakely-scale
earakely-scale deleted the edgararakelyan/openenv-adapter-contract branch October 27, 2026 18:26
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