Release OpenEnv 0.6.0 - #1211
cursor[bot] wants to merge 4 commits into
Conversation
Sets the published version to 0.6.0 for the Thursday 2026-09-24 cut. The only package change since v0.5.0 is the new NovitaSandboxProvider public surface, which a patch number would misrepresent; see the PR body for the version decision and outstanding blockers. Co-authored-by: benjamin.burtenshaw <benjamin.burtenshaw@huggingface.co>
Co-authored-by: benjamin.burtenshaw <benjamin.burtenshaw@huggingface.co>
grid_world_env is the only environment whose lock resolves the root openenv package from an editable path, so its uv.lock pins the project version. Left at 0.5.1.dev0 it makes `uv lock --check` fail in that directory; `uv sync --frozen` skips the freshness check, so no CI job catches it. Co-authored-by: benjamin.burtenshaw <benjamin.burtenshaw@huggingface.co>
There was a problem hiding this comment.
Candidate refreshed — state at fee469c4
I cannot edit this PR's body (no permission), so the description above is stale in
two ways. Current contents:
| Body says | Actually | |
|---|---|---|
| Candidate base | 39fdf5cd, 6 commits since v0.5.0 |
7e591317, 7 commits |
| Diff | version line only | version line + one lockfile line |
Comparison for the refreshed base: v0.5.0...7e59131
Added since opening: the GridWorld lock line
envs/grid_world_env/uv.lock is the only environment lock that resolves the root
openenv package from an editable path (source = { editable = "../../" }), so it
records the project version. With the version bump alone it is left at
0.5.1.dev0, and uv lock --check fails in that directory. After the one-line
change to 0.6.0 it passes, and uv sync --frozen --all-groups --all-extras --dry-run --no-install-project passes there too. Both verified locally.
No repository job catches this on its own: uv sync --frozen skips the freshness
check, and validate-env-locks is both scoped to changed env dependency files and
currently a no-op (it filters paths with rg, which is absent on ubuntu-latest,
so it logs rg: command not found and exits 0 — see #1197 for the detail). Treat
the green check on this PR as vacuous and the local run above as the real gate.
Follow-up for a maintainer: publish-pypi.yml's post-release-bump job edits
only pyproject.toml, so it will re-stale this lock the moment it writes
0.6.1.dev0. Every future version transition will need the same second line until
that job also refreshes the GridWorld lock.
Gate status on this exact head
All 10 reporting checks pass: test 3.11 and 3.12, test-thinkingbox-env 3.11 and
3.12, lint, check-env-docs, snapshot, Analyze (python), and Build and smoke-test package — the last one builds the wheel and sdist, runs twine check,
installs each into a clean venv, asserts openenv.__version__ equals the installed
metadata version, and runs openenv --help. mergeStateStatus is CLEAN.
A real Echo reset/step smoke also passes against this tree: reset metadata is
{'status': 'ready', 'message': 'Echo environment ready!'}, list_tools returns
both tools, the step envelope's data is the echoed message, call_tool returns
the bare string, and state reports step_count=1.
Still blocking publication
- Version decision —
0.6.0(recommended) or0.5.1. One line either way. - The Novita debt list in the body above needs an explicit ship-or-fix call.
publish-testpypi.ymlhas not been dispatched from this branch, and the fresh
production-PyPI install check obviously cannot run before publication.
Staying a draft until those clear. Nothing here should be read as a release
happening: this is a validated candidate, not a published version.
Sent by Cursor Automation: Release
There was a problem hiding this comment.
Independent release validation at fee469c4 — all local gates green
Ran the full release gate set off-CI against this exact head (base main 7e591317, unchanged since the last refresh). Recorded here because I cannot edit the PR body.
| Gate | Result |
|---|---|
uv build wheel + sdist |
openenv-0.6.0-py3-none-any.whl, openenv-0.6.0.tar.gz |
twine check |
PASSED both artifacts |
| Clean-venv wheel install | openenv 0.6.0, __version__ == installed metadata, openenv --help OK |
| Clean-venv sdist install | same, openenv --help OK |
openenv[novita] extra |
installs; NovitaSandboxProvider imports |
Tests (CI-equivalent: same ignores + not integration and not network and not docker) |
2761 passed, 95 skipped, 37 deselected |
Lint (CI-equivalent usort format + ruff format) |
no resulting git diff |
| Real Echo reset/step/state/call_tool | PASSED |
On lint: usort check alone reports tests/envs/test_grid_world.py and tests/envs/test_julia_env.py as unsorted, which reads like standing debt. It is not — CI runs usort format then ruff format, and ruff reverts usort's change, so the combined pipeline is a no-op and git diff is empty. No action needed.
Echo smoke covered reset metadata (status=ready), both advertised tools (echo_message, echo_with_length), a step whose payload echoes the sent message, step_count accounting, direct call_tool, rejection of an unknown tool, and a second reset clearing step_count to 0.
validate-env-locks passed vacuously on this PR
Worth stating explicitly since this PR changes a lockfile. The green check at fee469c4 (job log) contains:
rg: command not found
No environment dependency files changed.
The job filters changed paths with rg, which is not installed on ubuntu-latest, so mapfile yields an empty array and the job exits 0 without validating anything. The envs/grid_world_env/uv.lock change in this PR was never checked by CI. I validated it locally instead: uv lock --check resolves 117 packages cleanly and uv sync --frozen --all-groups --all-extras --dry-run --no-install-project succeeds.
Also confirmed while checking: grid_world_env is the only env lock with source = { editable = "../../" }, so it is the only one that tracks the root project version. The other 37 env locks pin published openenv versions and are unaffected by the bump.
Novita residual debt — triaged, recommend shipping
The one Tier 1 item that actually reaches the wheel is the unbraced ARG substitution order in _resolve_from_references. Reproduced against the shipped code:
| Dockerfile shape | Result |
|---|---|
ARG BASE then ARG BASE_IMAGE, FROM ${BASE_IMAGE} |
correct |
ARG BASE then ARG BASE_IMAGE, FROM $BASE_IMAGE |
FROM python:3.12_IMAGE |
| longer ARG declared first, unbraced | correct |
| single ARG, unbraced | correct |
So it needs a user Dockerfile with two global ARGs where one name is a prefix of the other, the shorter declared first, and the longer used unbraced. No in-repo Dockerfile can trigger it — there are zero FROM $ARG (unbraced) occurrences across envs/ and src/, and zero Dockerfiles with prefix-colliding global ARGs.
Recommendation: this is a latent bug in brand-new opt-in surface, not a regression, and does not warrant holding 0.6.0. Ship and track the fix (sort arg_defaults longest-name-first) as a follow-up.
Pre-existing, ships again, not a blocker
The README quick start is the PyPI long description, and its first example raises. Run verbatim against a live Echo server:
result.observation.echoed_message→AttributeError: 'GenericMCPObservation' object has no attribute 'echoed_message'. The text is atresult.metadata["message"].result.observation.resultis a dict, not the"Hello, World!"string the comment claims; the string is atresult.observation.result["data"].
This is not new — it is present at v0.4.2 and v0.5.0 and is already live on the PyPI 0.5.0 page. Flagging rather than fixing here, to keep this PR to the version change; it should not gate the release.
Still outstanding before this can merge
- Version call — 0.6.0 vs 0.5.1 (rationale in the PR body). One-line change either way.
- Ship-or-fix on the Novita debt — recommendation above is ship.
- TestPyPI dispatch — required by the release checklist and I get HTTP 403 on
workflow_dispatch, so this needs a maintainer to runpublish-testpypi.ymlfromcursor/openenv-release-management-7ba7.
Nothing is tagged or published. PyPI and the GitHub release remain 0.5.0.
Sent by Cursor Automation: Release
There was a problem hiding this comment.
Monday check-in — candidate re-validated, and the cargo list changed
main is still 7e591317 and this PR is still fee469c4, so the candidate did not drift over the weekend. I re-ran the release gate from scratch at this exact head rather than carrying Sunday's result forward:
| Gate | Result |
|---|---|
uv build |
openenv-0.6.0-py3-none-any.whl + openenv-0.6.0.tar.gz |
twine check |
PASSED both |
| clean-venv wheel install | 0.6.0, __version__ matches metadata, openenv --help OK |
| clean-venv sdist install | same |
openenv[novita] extra |
installs, NovitaSandboxProvider imports |
| tests (CI-equivalent flags) | 2761 passed, 95 skipped, 37 deselected |
| lint (CI-equivalent) | no resulting diff, ruff check clean |
grid_world_env lock |
uv lock --check resolves 117 packages; frozen dry-run clean |
| real Echo reset / step / state / list_tools | PASSED |
The grid_world_env lock was checked by hand again because CI's validate-env-locks still passes vacuously (rg: command not found → "No environment dependency files changed"), so this PR's own lockfile edit is not actually covered by the green check above it.
Cargo update: #1145 is now eligible
The "not in this candidate" list in the PR body needs one correction.
- #1145 is now approved from my side and it is package cargo — it changes
src/openenv/core/env_client.py. I withdrew my earlier change request after measuring the flagged edge case againstmaininstead of against an idealised baseline: on the path I called a regression,mainis equally wedged and additionally swallows the real configuration error, while this PR surfaces it. Full evidence is on that PR. It fixes the #1144 provider leak, so it is worth folding into 0.6.0 if its CI greens before the cut. - #1198 is also approved but stays out of the wheel — it is
envs/**plus one test. It makes #1174'sstate_clsfix actually reach users (onmain,maze_env's/schemaand/statedropcurrent_position,exit_cell,statusanddone, which the WebSocket frame returns). No packaging risk either way.
Both are fork PRs whose repository CI has never run, so neither can merge until a maintainer clicks Approve and run. That click has a lead time if either is to make Thursday.
Blockers unchanged
The three items in the PR body still need @burtenshaw: the version call (0.6.0 vs 0.5.1), Novita ship-or-fix (my recommendation is still ship — the unbraced $ARG collision is latent and no in-repo Dockerfile can trigger it), and the TestPyPI dispatch, which I cannot run (workflow_dispatch returns 403 for this token, and that workflow has no other trigger).
One housekeeping note on this PR's own text: the body says "Candidate base: 39fdf5cd … 6 commits since v0.5.0". It is now 7 commits and the base is 7e591317; the added commit is #1197, which the release notes already list. The notes are correct; only that one line is stale, and it will be rewritten when this PR is refreshed onto the final candidate head.
Nothing has been tagged. PyPI is still 0.5.0.
Sent by Cursor Automation: Release
Includes #1145 provider cleanup on failed connect. Co-authored-by: benjamin.burtenshaw <benjamin.burtenshaw@huggingface.co>


Release PR: v0.6.0
Planned release: Thursday, September 24, 2026 at 10:00 Europe/Brussels (08:00 UTC).
Candidate base:
39fdf5cd(main tip at the time of writing). 6 commits sincev0.5.0.This is the rolling release PR for the week; it will be refreshed onto the final
candidate head before merge.
Why 0.6.0 and not 0.5.1: the only package change since
v0.5.0is new publicsurface —
NovitaSandboxProvider(1,207 lines undersrc/openenv/core/containers/runtime/), a documented entry indocs/source/reference/core.md, and the newopenenv[novita]extra. That is addedfunctionality, which is a minor bump under the same standard applied when
0.5.0was cut rather than a quiet
0.4.3. If you would rather treat opt-in providers asnon-API and ship
0.5.1, it is a one-line change to this PR — please say so beforeWednesday so the notes and tag match.
Release notes
connect over
wss://, from either a registry image or a local Dockerfile(
image_from_dockerfile), with multi-stage Dockerfiles replayed as a single stagefor Novita's template parser. HTTPS/WSS transport is enforced and captured sandbox
output is withheld from raised errors unless
surface_server_logs=True. Installsvia
pip install openenv[novita](#1191).Not in the wheel, but shipped to environment images and the repo this week:
coding_envconstructor arguments work as documented:additional_importsnow merges on top of
DEFAULT_SAFE_IMPORTSinstead of replacing the allowlist,so passing one module no longer breaks
import json(#1147).
TLSStreamIDNA certificate-spoofing advisory plus two lower-severity fixes(#1201,
#1202, and
#1197 for the remaining 31
lockfiles), and soupsieve 2.9.2 for the polynomial-ReDoS advisories
(#1196, extended to
browsergym_envandfinrl_envin chore(deps): aggregate envs Dependabot updates #1197).Full candidate comparison: v0.5.0...39fdf5c
Release-maintenance changes in this PR
0.5.1.dev0to stable0.6.0. Nothing else.Outstanding blockers
0.6.0(recommended) or say0.5.1and I willchange the one line.
opt-in Novita provider and its examples, none of it re-blocking in my
assessment — listed so the decision to ship it is explicit:
unbraced
$ARGsubstitution during Dockerfile flattening can corrupt aFROMline (
$BASEmatching before$BASE_IMAGE);examples/novita_tbench2_simple.pycalls
wait_for_readyoutside itstry/finally, so a readiness timeout leaks apaid sandbox; concurrent starts and failed kills can orphan sandboxes; flattened
stages carry builder filesystem/ENV/USER state into the runtime image; and the
RFC 002 provider amendment (root execution, unrestricted egress, HTTP-only
readiness) is still unratified.
publish-testpypi.ymlfrom this exact branch with a unique0.6.0pre/dev suffix.mainimmediately before merge; rerun all required checks if thehead or base changes.
Not in this candidate: every open small fix that touches the package
(#1145,
#1198,
#1200,
#1203,
#1208,
#1209) comes from a fork whose
workflows are still
action_required, so none of them has ever run repository CI.A maintainer clicking Approve and run is the only way any of them becomes
release-eligible.
Release Checklist
Before opening this PR
pyproject.tomlversion changed from0.5.1.dev0→0.6.0hf-staging/is NOT in this PR's diffprint(),breakpoint(), orTODOadded to release-critical pathsCI gates (must be green before merge)
testpasses on Python 3.11testpasses on Python 3.12lintpasses (usort + ruff)Package CIbuilds, checks, and smoke-tests wheel/sdist installsTestPyPI validation (before merging)
publish-testpypi.ymlfrom this branch0.6.0.devNpip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ openenv==0.6.0.devNPost-merge steps
v0.6.0pushed against the exact merge commit onmainpublish-pypi.ymlcompleted successfully from the tagpip install openenv==0.6.0from production PyPI verified (import,__version__,openenv --help, real Echo reset/step)RFC Status