Skip to content

fix(envs): serve each environment's own State subclass on /schema and /state - #1198

Merged
burtenshaw merged 40 commits into
openenv:mainfrom
k21993:feat/env-state-cls
Oct 19, 2026
Merged

burtenshaw merged 40 commits into
openenv:mainfrom
k21993:feat/env-state-cls

Conversation

@k21993

@k21993 k21993 commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

#1174 added state_cls to the app factories, but callers were not passing it. This wires all 28 environments with custom State subclasses and the package-backed Harbor app to their own classes, so /schema and /state preserve the same fields as the WebSocket state frame.

Refreshed against main at 5639e52a. The merge preserves upstream concurrency settings and removal of opencode_env and pi_env, and wires the newly added tau2_env. Existing signature guards in repl_env and textarena_env preserve compatibility with older OpenEnv installations.

Follow-up to #1174, which closed #1155.

Type of Change

  • Bug fix
  • New feature
  • Breaking change

Alignment Checklist

  • Reviewed principles and invariants
  • Call-site wiring only; no API changes
  • Static environment coverage and real HTTP coverage for Harbor

RFC Status

  • Not required: existing state serialization contract, no API changes

Test Plan

  • Environment state-wiring and Harbor HTTP tests: 38 passed. The scanner found the missing tau2 wiring before it was added.
  • Broad non-integration suite: 3,697 passed, 130 skipped, 60 deselected. The remaining Harbor port-conflict test fails on this macOS host; see validation notes below.
  • Changed-file Ruff formatting, Ruff lint, usort, and diff whitespace checks pass.
  • Environment documentation sync passes.
  • Repository-wide formatting reports existing issues also present on clean main, including the two known usort files. No unrelated formatting changes are included.

Notes for review

The environment regression scans app factory calls with ast, so optional simulator dependencies are not needed. It checks every factory call and resolves signature-guarded kwargs. Harbor has a separate TestClient regression for both /schema and /state because its app lives under src/openenv/harbor/.

thinkingbox_env passes its class directly to HTTPEnvServer; /schema remains relevant in production mode. Environments without custom State subclasses keep the base class.

Validation notes: the Harbor port-conflict failure reproduces on clean main (5639e52a) with the same error: expected already in use, received a startup timeout. No new failures were found in the broad suite.


Note

Low Risk
Call-site wiring only with no API signature changes; behavior change is richer HTTP state/schema payloads aligned with WebSocket, with regression tests guarding future omissions.

Overview
Wires every environment (and Harbor) that defines a custom State subclass to pass state_cls into create_app / HTTPEnvServer, so HTTP /schema and /state expose the same fields as WebSocket state instead of falling back to the base State (episode_id, step_count only).

Coverage spans all listed envs/*/server/app.py servers plus openenv.harbor.serving.build_app and thinkingbox_env’s direct HTTPEnvServer construction. repl_env and textarena_env only set state_cls when inspect.signature(create_app) includes the parameter, keeping older OpenEnv installs bootable.

Adds an ast-based regression test that scans env server/app.py for factory calls missing or mis-wiring state_cls, and a Harbor TestClient check that /schema and /state include HarborState fields (e.g. llm_url).

Reviewed by Cursor Bugbot for commit 993aa2d. Bugbot is set up for automated code reviews on this repo. Configure here.

openenv#1174 added state_cls to the app factories, but nothing passes it, so the 28
envs that declare a State subclass still publish a state schema of episode_id
and step_count and still strip every subclass field from the /state body. Their
WebSocket state frame returns the full object, so the two transports disagree.

Pass each env's State subclass at its app factory call site, and add a static
regression test that walks envs/ with ast, so a new env cannot quietly skip it.

Follow-up to openenv#1174.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread envs/repl_env/server/app.py Resolved
Comment thread tests/envs/test_env_state_cls_wiring.py Resolved
repl_env and textarena_env pick between two create_app calls by probing
inspect.signature(create_app). The first branch is the one that runs against a
current openenv; the second exists for a release predating gradio_builder. The
previous commit wired only the second, so /schema and /state still served the
base State model for these two envs.

Pass state_cls in the live branch, guarded by the same signature probe the file
already uses for its other newer kwargs, and drop it from the legacy branch,
where an openenv old enough to take that path would reject the argument.

The test inspected only the last factory call in a module, which is why it did
not catch this. It now checks every call, resolves values passed through a
splatted kwargs dict, and exempts calls inside a signature-guarded fallback.
@k21993

k21993 commented Sep 18, 2026

Copy link
Copy Markdown
Contributor Author

Both findings were right, thanks. repl_env and textarena_env select between two create_app calls with inspect.signature, and I had wired only the legacy fallback, so the branch that actually runs still served the base State model.

Fixed in 1a84a1b0: the live branch passes state_cls behind the same signature guard the file already uses for its other newer kwargs, and the legacy branch no longer passes it at all. Both envs pin openenv>=0.2.2, so an installation old enough to take that path would reject the argument outright.

The test had the same blind spot, which is why it stayed green through the first commit. It now checks every factory call in a module rather than the last one, resolves values passed through a splatted kwargs dict, and exempts only calls inside a signature-guarded fallback. Removing the guard from repl_env's live branch now fails that env's case.

@cursor cursor Bot mentioned this pull request Sep 19, 2026
5 of 21 tasks
cursor[bot]
cursor Bot approved these changes Sep 21, 2026 •
@burtenshaw

Copy link
Copy Markdown
Collaborator

cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

REQUEST_CHANGES at 3b085d82 (Ben ask: cursor review)

The 28 env app.py wirings + repl/textarena branch logic + AST test are correct — 29/29 wiring tests pass. One completeness gap remains:

Tier 1 — Harbor still unwired

src/openenv/harbor/serving.py build_app() still calls create_app(HarborEnvironment, …) without state_cls=HarborState. HarborState lives under src/openenv/harbor/models.py, so the test’s envs/-only scanner finds no State subclass and silently skips Harbor. Live Harbor /schema and /state still drop the custom fields.

This PR does not touch Harbor, but the title/claim is “each environment’s own State subclass.” Please either:

  1. Wire state_cls=HarborState in build_app() and extend the scanner to src/openenv/<name>/ (or an allowlist), or
  2. Explicitly carve Harbor out of the claim with a tracked follow-up.

Not 0.6.0 wheel cargo (envs/** + test only; Harbor fix would be package cargo if included). Branch was refreshed onto main 228bd147.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Release

@burtenshaw

Copy link
Copy Markdown
Collaborator

Addressed the Harbor review in da82e56. build_app() now passes state_cls=HarborState. Added a focused real-HTTP regression for both /schema and /state in the existing Harbor serving tests; the generic scanner is unchanged.

The new test failed before the fix because all six Harbor-specific fields were missing. After the fix, the Harbor serving, environment wiring, and core state-schema suites passed: 46 tests. Touched-file usort, Ruff format/check, and git diff --check passed. The fix includes src/openenv/harbor/serving.py, so it is package code as well as environment wiring.

Fork workflows still require approval to run; no CI approval or rerun was performed.

@burtenshaw

Copy link
Copy Markdown
Collaborator

cursor review

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

APPROVE at da82e564

Prior Harbor blocker is fixed: build_app() now passes state_cls=HarborState, and test_harbor_app_serves_its_own_state_class asserts /schema + /state retain Harbor fields over HTTP (llm_url observed).

Validation:

  • tests/envs/test_harbor_hosted_serving.py + test_env_state_cls_wiring.py: 38/38 pass
  • lint/format clean on the touched files

Because this changes src/openenv/harbor/serving.py, treat as 0.6.0 package cargo if merged (refresh draft #1211 after green exact-head CI). Still needs fork Approve-and-run / repository checks before merge.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Release

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

APPROVE at 7a967005 (merge-main only)

Rebased onto main after #1178. Feature tip unchanged from prior APPROVE at da82e564: Harbor build_app() still passes state_cls=HarborState (serving.py). 0.6.0 package cargo if merged — refresh draft #1211 after green exact-head CI.

Still needs fork Approve-and-run / repository CI.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Release

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

APPROVE at e438c498 (merge-main only)

Rebased onto main after #1219. Feature unchanged: Harbor state_cls=HarborState wiring remains.

Package cargo if merged (would require #1211 refresh/TestPyPI). Fork may still need Approve-and-run.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Release

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Merge-main re-APPROVE at 4ace122a

Ben merged main into this fork tip. Feature diff unchanged: env state_cls wiring + Harbor build_app(... state_cls=HarborState) with regressions.

Fork PR: repository CI still needs Approve-and-run. OpenEnv wheel cargo if merged before Thursday — would require refreshing draft #1211 + TestPyPI.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Release

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-APPROVE at 29b780c1 (merge-main only)

Ben merged main (26c9465e / #1179) into this fork PR. Stable feature patch-id is unchanged vs prior approved tip 4ace122a (9fc93a50…); Harbor/state_cls wiring + tests identical.

Prior technical approval stands. Still needs maintainer Approve and run. Package cargo if merged before Thursday — prefer hold through 0.6.0 to avoid another candidate/TestPyPI churn.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Release

@k21993

k21993 commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

@burtenshaw exact head 6c28c503 contains only merges from main after the approved Harbor fix. I validated GitHub's exact archive: the focused HTTP/state suite passed 46 tests; core passed 684 with 1 skipped; manifest targets passed 78; changed-file Ruff and usort checks passed; and the environment docs sync is clean.

The five repository workflows are still at action_required and ran no jobs. Could you approve them when you have a chance? Once exact-head CI is green, this should be ready to merge and include in #1211.

@cursor cursor Bot mentioned this pull request Sep 25, 2026
4 of 16 tasks

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread tests/envs/test_env_state_cls_wiring.py Resolved
@burtenshaw

Copy link
Copy Markdown
Collaborator

cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 55d7947. Configure here.

@burtenshaw burtenshaw mentioned this pull request Oct 5, 2026
7 of 14 tasks
k21993 and others added 2 commits October 14, 2026 17:32
@bot-ci-comment

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@burtenshaw burtenshaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed state-class wiring and schema behavior; 129 focused environment, Harbor, and core schema tests pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/schema and /state always use the base State model, dropping every field an environment's State subclass declares

2 participants