Skip to content

[diff view] RFC 005 3/4: environment runtime - #2

Closed
splusq wants to merge 1 commit into
rfc-005/pr2-harness-foundation-typesfrom
rfc-005/pr3-harness-environment-runtime
Closed

[diff view] RFC 005 3/4: environment runtime#2
splusq wants to merge 1 commit into
rfc-005/pr2-harness-foundation-typesfrom
rfc-005/pr3-harness-environment-runtime

Conversation

@splusq

@splusq splusq commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Incremental-diff view for huggingface/OpenEnv#1099.

Review and discussion happen upstream — this PR exists only because cross-fork PRs cannot chain bases, so the upstream one has to target main and its "Files changed" tab includes the PRs beneath it in the stack.

Here the base is rfc-005/pr2-harness-foundation-types, so Files changed shows exactly this step's diff (commit f3d90c21) and nothing else.

Not for merging: merging it into the fork would desync the stack.

🤖 Generated with Claude Code

…idge

Makes the RFC 005 types runnable: an environment that owns a harness
subprocess, hands it the environment's MCP tools, and turns each step()
into one conversational turn.

- `environment.py`: `HarnessAction` + `HarnessEnvironment(MCPEnvironment)`.
  reset() stops any live harness, enumerates and conflict-resolves the env
  tools, starts the bridge, injects, then starts the harness -- injection
  strictly before start, per the RFC. step() runs one turn; MCP actions
  keep their normal routing. Rubrics run after the turn completes, outside
  the harness's control loop, preserving RFC 004's reward boundary.
- `process.py`: `HarnessProcess`, a loop-agnostic Popen + reader-thread
  helper (readiness gating, stderr-tail diagnostics, idempotent stop with
  SIGTERM -> SIGKILL escalation over the process group).
- `bridge.py`: `HarnessMCPBridge`, serving the env's FastMCP tool surface
  over loopback HTTP for the harness to consume.

Three decisions worth reviewer attention:

1. `HarnessEnvironment` subclasses `MCPEnvironment` and substitutes an
   empty internal FastMCP when `mcp=None`. `MCPEnvironment` requires
   `mcp_server` positionally, so the RFC's optional-mcp constructor cannot
   be written literally; this keeps reserved-name validation, tool
   enumeration and mcp_session() integration for free.
2. Popen + threads rather than asyncio subprocess transports, because the
   same instance must work across event loops: the sync facade spins a
   fresh loop per call (run_async_safely) while the server keeps one
   long-lived loop. Asyncio subprocess transports are bound to their
   creating loop.
3. The bridge is a separate loopback server rather than a reuse of the
   env server's /mcp endpoint. Reusing /mcp would put the orchestration
   routes (/reset, /step, /state) on the same origin the harness can
   reach, violating the RFC's security boundary; it would also hand the
   harness a *different* env instance, since WS /mcp creates its own
   session. Keeping it separate makes the boundary structural rather than
   filter-based.

Turn timeouts and harness crashes become terminal observations
(done=True, metadata.error_type) rather than exceptions, so a training
loop scores the episode and moves on instead of unwinding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@splusq

splusq commented Aug 28, 2026

Copy link
Copy Markdown
Owner Author

Closing: this was only a diff view, and an un-mergeable PR in the list is more confusing than the cumulative diff it was working around. Review happens on the upstream PR.

@splusq splusq closed this Aug 28, 2026
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.

1 participant