Skip to content

Add minesweeper env and example#236

Open
acharyaanusha wants to merge 26 commits into
huggingface:mainfrom
acharyaanusha:feature/minesweeper_env
Open

Add minesweeper env and example#236
acharyaanusha wants to merge 26 commits into
huggingface:mainfrom
acharyaanusha:feature/minesweeper_env

Conversation

@acharyaanusha

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new Minesweeper game environment to OpenEnv, providing a classic grid-based puzzle game for reinforcement learning agents. The environment challenges agents to reveal all non-mine cells without triggering any mines, using strategies based on number clues and flag placement.

Motivation

Minesweeper is a well-known logical puzzle that requires:

  • Strategic thinking and planning
  • Risk assessment and decision-making under uncertainty
  • Multi-step reasoning based on partial information
  • Balance between exploration (revealing cells) and exploitation (using known information)

This environment is valuable for:

  • Testing RL agents on logical reasoning tasks
  • Evaluating agents' ability to handle uncertainty and risk
  • Benchmarking decision-making in partially observable environments
  • Educational purposes and demonstrations

Changes

Core Implementation

  • Environment Logic: Full Minesweeper game implementation with configurable grid size and mine count
  • Action Space: Two action types:
    • reveal: Uncover a cell to see its value
    • flag: Place/remove flag on suspected mine locations
  • Observation Space: 2D grid showing:
    • Unrevealed cells (-1)
    • Number clues (0-8) indicating adjacent mines
    • Flagged cells ('F')
    • Mines ('*', only shown on game over)
  • Reward System:
    • +1.0 for revealing safe cells
    • +0.5 for correctly flagging mines
    • -10.0 for hitting mines (game over)
    • Small penalties for invalid actions
  • Game States: ONGOING, WON, LOST

Features

  • Recursive Reveal: Automatically reveals adjacent cells when a cell with zero adjacent mines is uncovered
  • State Management: Full game state tracking with episode IDs and step counts
  • HTTP API: FastAPI-based server for remote environment access
  • Type Safety: Pydantic models for all actions and observations

Docker Support

  • Multi-stage Dockerfile based on openenv-base image
  • Build script (build_docker.sh) for easy local development
  • Health check endpoint for container orchestration
  • Optimized layer caching for faster builds

Testing

  • Comprehensive test suite with 13 test cases covering:
    • Server setup and initialization
    • Action execution (reveal, flag, toggle)
    • Edge cases (invalid positions, already revealed cells)
    • Game state transitions
    • Reset functionality
    • HTTP serialization handling
  • All tests passing ✅

Documentation

  • Detailed README with:
    • Quick start guide
    • Action and observation specifications
    • Reward structure
    • Configuration options
    • Usage examples
    • Docker instructions
  • Example agent script demonstrating full gameplay

CI/CD Integration

  • Added to GitHub Actions build matrix
  • Automated Docker image building for linux/amd64 and linux/arm64
  • Automated publishing to GitHub Container Registry

Files Changed

New Files (14 files, 1,552 insertions)

Environment Core:

  • src/envs/minesweeper_env/__init__.py - Module exports and public API
  • src/envs/minesweeper_env/client.py - HTTP client for remote environment access
  • src/envs/minesweeper_env/models.py - Pydantic models for actions, observations, and state
  • src/envs/minesweeper_env/openenv.yaml - Environment metadata and configuration
  • src/envs/minesweeper_env/pyproject.toml - Package dependencies and build configuration

Server Implementation:

  • src/envs/minesweeper_env/server/__init__.py - Server module exports
  • src/envs/minesweeper_env/server/app.py - FastAPI application and HTTP endpoints
  • src/envs/minesweeper_env/server/minesweeper_environment.py - Core game logic and environment implementation

Docker & Build:

  • src/envs/minesweeper_env/server/Dockerfile - Multi-stage container image definition
  • src/envs/minesweeper_env/server/build_docker.sh - Build script for local development

Documentation & Examples:

  • src/envs/minesweeper_env/README.md - Comprehensive environment documentation
  • examples/minesweeper_agent.py - Example agent demonstrating environment usage

Testing:

  • tests/envs/test_minesweeper_env.py - Test suite with 13 test cases

Modified Files (1 file)

CI/CD:

  • .github/workflows/docker-build.yml - Added minesweeper-env to build matrix for automated Docker image building

Usage Example

from envs.minesweeper_env import MinesweeperAction, MinesweeperEnv

# Create environment from Docker image
env = MinesweeperEnv.from_docker_image("minesweeper-env:latest")

try:
    # Reset
    result = env.reset()
    print(f"Board: {result.observation.board_height}x{result.observation.board_width}")
    print(f"Mines: {result.observation.num_mines}")
    
    # Reveal a cell
    result = env.step(MinesweeperAction(row=2, col=2, action_type="reveal"))
    
    # Place a flag
    result = env.step(MinesweeperAction(row=0, col=0, action_type="flag"))
    
finally:
    env.close()

Testing

Run the test suite:

python tests/envs/test_minesweeper_env.py

Build and run the Docker image:

./build_docker.sh latest
docker run -p 8000:8000 minesweeper-env:latest

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Dec 5, 2025
@meta-cla

meta-cla Bot commented Dec 24, 2025

Copy link
Copy Markdown

Hi @acharyaanusha!

Thank you for your pull request.

We require contributors to sign our Contributor License Agreement, and yours needs attention.

You currently have a record in our system, but the CLA is no longer valid, and will need to be resubmitted.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

Darktex
Darktex previously approved these changes Jan 13, 2026

@Darktex Darktex 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.

Note: This is an automated review by Claude Code (alignment-reviewer agent), not a human review. The account posting this is shared with the human maintainer.



Automated review by Claude Code | Learn more about OpenEnv's agentic workflow

@Darktex Darktex 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.

Note: This is an automated review by Claude Code (alignment-reviewer agent), not a human review. The account posting this is shared with the human maintainer.


Alignment Review Report for PR #236

Executive Summary

This PR adds a new Minesweeper game environment with comprehensive implementation. The code is well-structured and follows OpenEnv patterns, but there are critical issues that must be addressed before merging.

Severity Breakdown:

  • 🔴 Critical (Tier 1): 5 issues requiring immediate fixes
  • 🟡 Alignment Concerns (Tier 2): 3 items flagged for human review

Tier 1: Fixes Required (Immediate Action)

🔴 1. Client Uses HTTPEnvClient Instead of EnvClient (WebSocket)

Location: src/envs/minesweeper_env/client.py:22

class MinesweeperEnv(HTTPEnvClient[MinesweeperAction, MinesweeperObservation]):

Issue: The environment uses the deprecated HTTPEnvClient base class instead of the modern EnvClient (WebSocket-based) that all other environments use.

Evidence from reference implementation (envs/echo_env/client.py:31):

class EchoEnv(EnvClient[EchoAction, EchoObservation, State]):

Why this matters:

  • According to INVARIANTS.md line 73-74: "We are in the process of deprecating HTTP in favor of WebSocket-only"
  • All existing environments (echo, chat, etc.) use EnvClient with WebSocket
  • This creates inconsistency and technical debt

Required fix: Change to inherit from EnvClient and implement WebSocket communication like echo_env does.


🔴 2. Incorrect Import Paths (openenv_core vs openenv.core)

Locations: Multiple files

  • src/envs/minesweeper_env/client.py:509-511
  • src/envs/minesweeper_env/models.py:620
  • src/envs/minesweeper_env/server/app.py:905
  • src/envs/minesweeper_env/server/minesweeper_environment.py:1049-1050

Issue: Uses openenv_core imports instead of openenv.core pattern used by all other environments.

Examples:

# ❌ Wrong (minesweeper_env)
from openenv_core.client_types import StepResult
from openenv_core.env_server.types import Action, Observation

# ✅ Correct (echo_env reference)
from openenv.core.client_types import StepResult
from openenv.core.env_server.types import Action, Observation

Evidence: All existing environments (echo, chat, etc.) use the openenv.core import pattern with fallback handling for both in-repo and standalone modes.

Required fix: Update all imports to match the pattern in envs/echo_env/ with proper try/except for in-repo vs standalone imports.


🔴 3. Missing Copy-Paste Documentation Updates

Location: Multiple files

Issues found:

  1. client.py:527-532 - Docstring still mentions "echoed_message" from echo_env template:

    >>> print(result.observation.echoed_message)  # ❌ Wrong - should be board/game_status
    >>> result = client.step(MinesweeperAction(message="Hello!"))  # ❌ Wrong action signature
  2. models.py:614 - Module docstring says "echoes back messages":

    """
    Data models for the Minesweeper Env Environment.
    
    The minesweeper_env environment is a simple test environment that echoes back messages.  # ❌ Wrong!
    """
  3. server/minesweeper_environment.py:1033-1036 - Docstring says "echoes back messages":

    """
    Minesweeper Environment Implementation.
    
    A simple test environment that echoes back messages sent to it.  # ❌ Wrong!
    Perfect for testing HTTP server infrastructure.
    """

Required fix: Update all docstrings to accurately describe Minesweeper functionality, not echo functionality.


🔴 4. Docker Workflow Merge Conflict

Location: .github/workflows/docker-build.yml:82-84

Issue: The PR introduces a duplicate/malformed entry in the build matrix:

- name: my-env  # Add your environment here
  dockerfile: src/envs/connect4_env/server/Dockerfile  # ❌ Added by this PR
- name: minesweeper-env                                  # ✅ Also added by this PR
  dockerfile: src/envs/minesweeper_env/server/Dockerfile
  dockerfile: envs/connect4_env/server/Dockerfile     # ❌ Duplicate key from merge

Why this happened: The PR incorrectly modified the template placeholder line AND added a new line, creating a malformed YAML structure.

Required fix:

  • Remove the my-env modifications
  • Keep only the clean minesweeper-env entry
  • Ensure the dockerfile path is correct

🔴 5. Missing Newline at End of Files

Locations:

  • src/envs/minesweeper_env/openenv.yaml:743 - "\ No newline at end of file"
  • src/envs/minesweeper_env/pyproject.toml:782 - "\ No newline at end of file"
  • src/envs/minesweeper_env/server/Dockerfile:856 - "\ No newline at end of file"
  • src/envs/minesweeper_env/server/minesweeper_environment.py:1348 - "\ No newline at end of file"

Required fix: Add newline character at end of each file (POSIX standard).


Tier 2: Alignment Discussion

🟡 1. Models Use dataclass Instead of Pydantic

Location: src/envs/minesweeper_env/models.py:617-659

The pattern:

@dataclass(kw_only=True)
class MinesweeperAction(Action):
    row: int
    col: int
    action_type: str  # 'reveal' or 'flag'

Reference implementation (echo_env):

class EchoAction(Action):
    message: str = Field(..., min_length=1, description="Message to echo back")

Principle at stake: "Type safety with generics and Pydantic across the wire" (PRINCIPLES.md line 16)

The concern:

  • Echo environment uses Pydantic models directly (inheriting from Action/Observation which are Pydantic BaseModels)
  • Minesweeper uses @dataclass decorator on top of these Pydantic base classes
  • This may cause issues with:
    • Validation (no Field constraints like min_length, value validation)
    • Serialization behavior
    • API consistency

Questions for human reviewer:

  1. Is mixing dataclass with Pydantic BaseModel inheritance intentional?
  2. Should action_type have validation (e.g., Literal["reveal", "flag"])?
  3. Does this pattern work correctly with Pydantic serialization?

Suggested reviewer: @Darktex


🟡 2. HTTP-Only Environment in WebSocket Transition Period

Principle at stake: "WebSocket for all environment communication" (INVARIANTS.md line 69-74)

The concern:

  • INVARIANTS.md states: "We are in the process of deprecating HTTP (see PR #252) in favor of WebSocket-only, but we are still transitioning and both protocols are currently available."
  • This new environment is built entirely on HTTP infrastructure
  • Adds new technical debt during a known deprecation period
  • Will require refactoring work when HTTP is fully deprecated

Questions for human reviewer:

  1. Should we accept new HTTP-only environments during the WebSocket transition?
  2. If yes, should the PR include a TODO or tracking issue for WebSocket migration?
  3. Is there a timeline for completing the HTTP deprecation that should inform this decision?

Suggested reviewer: @Darktex


🟡 3. Environment Location Inconsistency

The pattern:

  • PR places environment in: src/envs/minesweeper_env/
  • Existing environments are in: envs/echo_env/, envs/chat_env/, envs/coding_env/, etc.

Principle at stake: "One canonical way to build environments" (PRINCIPLES.md line 21)

The concern:

  • All reference environments are in envs/ directory
  • This new environment is in src/envs/ directory
  • Creates inconsistency in repository structure
  • Dockerfile path uses src/envs/minesweeper_env/ which differs from existing patterns

Questions for human reviewer:

  1. Is there a new convention to move environments to src/envs/?
  2. Should existing environments migrate to this location?
  3. Or should this PR move minesweeper to envs/ to match existing convention?

Suggested reviewer: @Darktex


Automated Checks

Debug code check: CLEAN - No print statements, breakpoints, or TODO comments found in minesweeper files

⚠️ Lint check: Unable to verify - uv command not available in environment


Positive Aspects

The PR does many things well:

  1. Comprehensive testing: 13 test cases covering edge cases, state management, serialization
  2. Good documentation: Detailed README with examples, API specs, usage instructions
  3. Clean game logic: Well-structured recursive reveal, proper state management
  4. Reward design: Sensible reward structure aligned with RL objectives
  5. Example agent: Demonstrates full usage with a simple but functional strategy
  6. Docker support: Multi-stage build with health checks
  7. No agent-accessible reset: Correctly does not expose reset/step to agents via MCP (maintains invariant)

Summary

Tier 1 Issues: 5 mechanical issues requiring fixes

  • Client architecture (HTTP vs WebSocket)
  • Import paths (openenv_core vs openenv.core)
  • Copy-paste documentation errors
  • Docker workflow YAML structure
  • Missing newlines

Tier 2 Issues: 3 alignment questions requiring human decision

  • dataclass vs pure Pydantic pattern
  • Adding HTTP-only env during WebSocket transition
  • Environment location convention (src/envs/ vs envs/)

Recommendation: Request changes for Tier 1 fixes before merge. Tier 2 items need maintainer input on project direction.


Automated review by Claude Code | Learn more about OpenEnv's agentic workflow

@Darktex Darktex dismissed their stale review January 13, 2026 05:51

Dismissing automated approval due to bug in review bot. The original review either had blank content or approved despite finding blocking issues. Please disregard this approval.

@acharyaanusha

Copy link
Copy Markdown
Contributor Author

@Darktex fixed all the claude code comments. Should be ready for another review

@zkwentz

zkwentz commented Jan 21, 2026

Copy link
Copy Markdown
Collaborator

@greptile

@greptile-apps

greptile-apps Bot commented Jan 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a complete Minesweeper environment implementation following OpenEnv patterns, including core game logic, client/server architecture, Docker support, comprehensive tests, and example agent code.

Key Changes:

  • Implemented full Minesweeper game with reveal/flag actions, recursive cell revealing, and win/loss conditions
  • Created WebSocket-based client (MinesweeperEnv) and FastAPI server following OpenEnv architecture
  • Added Docker support with multi-stage build and health checks
  • Integrated into CI/CD pipeline for automated image building
  • Provided comprehensive test suite (13 tests) and example agent demonstrating strategy
  • Included detailed documentation with action space, reward structure, and usage examples

Issues Found:

  • Critical: Dockerfile CMD uses incorrect module path minesweeper.server.app:app that will fail at runtime (should be server.app:app)
  • Logic bug: In _reveal_cell, the cell is added to _revealed_cells on line 185 before calling _reveal_recursive, which adds it again on line 207, causing redundant operations
  • Test design: Tests start a new server for each test method instead of sharing one instance, causing port conflicts and slow test execution

Strengths:

  • Clean separation of client/server following OpenEnv invariants
  • Well-structured Pydantic models with proper validation
  • Good documentation and example code
  • Follows established patterns from other environments

Confidence Score: 2/5

  • This PR has critical issues that will prevent the Docker container from starting
  • Score reflects a critical Dockerfile bug that will cause runtime failure when the container starts. The incorrect module path in the CMD directive means the container will immediately crash. Additionally, there's a logic bug in the cell reveal function and test architecture issues that need addressing.
  • Pay close attention to src/envs/minesweeper_env/server/Dockerfile (incorrect CMD path will break container startup) and src/envs/minesweeper_env/server/minesweeper_environment.py (redundant cell addition logic)

Important Files Changed

Filename Overview
src/envs/minesweeper_env/server/minesweeper_environment.py Core game logic with recursive reveal bug and redundant cell additions
src/envs/minesweeper_env/client.py Clean client implementation following OpenEnv patterns correctly
src/envs/minesweeper_env/server/app.py Minimal FastAPI app setup following standard pattern
tests/envs/test_minesweeper_env.py Comprehensive test coverage but with test dependencies and server management issues
src/envs/minesweeper_env/server/Dockerfile Dockerfile with incorrect CMD module path that will fail at runtime

Sequence Diagram

sequenceDiagram
    participant Agent
    participant Client as MinesweeperEnv<br/>(Client)
    participant Server as FastAPI Server
    participant Env as MinesweeperEnvironment

    Note over Agent,Env: Connection Establishment
    Agent->>Client: MinesweeperEnv(base_url)
    Client->>Server: WebSocket connect
    Server->>Env: Create environment instance
    Server-->>Client: Connection established

    Note over Agent,Env: Reset Episode
    Agent->>Client: reset()
    Client->>Server: WS: reset message
    Server->>Env: reset()
    Env->>Env: Place mines randomly
    Env->>Env: Compute mine counts
    Env-->>Server: MinesweeperObservation<br/>(board=-1, status=ONGOING)
    Server-->>Client: WS: observation response
    Client-->>Agent: StepResult(observation)

    Note over Agent,Env: Game Loop - Reveal Action
    Agent->>Client: step(MinesweeperAction<br/>row=2, col=2, reveal)
    Client->>Server: WS: step message with action
    Server->>Env: step(action)
    Env->>Env: Validate position
    Env->>Env: _reveal_cell(2,2)
    alt Cell is mine
        Env->>Env: Set status=LOST
        Env->>Env: Add to revealed_cells
        Env-->>Server: Observation(reward=-10.0, done=true)
    else Cell is safe
        Env->>Env: _reveal_recursive(2,2)
        Env->>Env: Add to revealed_cells
        alt mine_count == 0
            Env->>Env: Recursively reveal neighbors
        end
        Env->>Env: _check_win_condition()
        Env-->>Server: Observation(reward=1.0, done=false)
    end
    Server-->>Client: WS: observation response
    Client-->>Agent: StepResult(observation, reward, done)

    Note over Agent,Env: Game Loop - Flag Action
    Agent->>Client: step(MinesweeperAction<br/>row=0, col=0, flag)
    Client->>Server: WS: step message with action
    Server->>Env: step(action)
    Env->>Env: _toggle_flag(0,0)
    alt Cell already flagged
        Env->>Env: Remove from flags
        Env-->>Server: Observation(reward=0.0)
    else Cell not flagged
        Env->>Env: Add to flags
        alt Cell is mine
            Env-->>Server: Observation(reward=0.5)
        else Cell not mine
            Env-->>Server: Observation(reward=0.0)
        end
    end
    Server-->>Client: WS: observation response
    Client-->>Agent: StepResult(observation, reward, done)

    Note over Agent,Env: Session Cleanup
    Agent->>Client: close()
    Client->>Server: WS: close message
    Server->>Env: Cleanup resources
    Server-->>Client: Connection closed
Loading

@greptile-apps greptile-apps 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.

14 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +180 to +195
def _reveal_cell(self, row: int, col: int) -> float:
"""Reveal the cell at (row, col). Returns the reward for the action."""
if (row, col) in self._revealed_cells or (row, col) in self._flags_placed:
return -0.05 # Penalty for revealing already revealed or flagged cell

self._revealed_cells.add((row, col))

if (row, col) in self._mine_positions:
self._game_status = GameStatus.LOST
self._revealed_cells.add((row, col))
return -10.0 # Penalty for hitting a mine

# Reveal the cell and potentially adjacent cells if count is 0
self._reveal_recursive(row, col)

return 1.0 # Small reward for safe reveal

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.

logic: cell added to _revealed_cells on line 185, then _reveal_recursive adds it again on line 207, causing redundant addition

Suggested change
def _reveal_cell(self, row: int, col: int) -> float:
"""Reveal the cell at (row, col). Returns the reward for the action."""
if (row, col) in self._revealed_cells or (row, col) in self._flags_placed:
return -0.05 # Penalty for revealing already revealed or flagged cell
self._revealed_cells.add((row, col))
if (row, col) in self._mine_positions:
self._game_status = GameStatus.LOST
self._revealed_cells.add((row, col))
return -10.0 # Penalty for hitting a mine
# Reveal the cell and potentially adjacent cells if count is 0
self._reveal_recursive(row, col)
return 1.0 # Small reward for safe reveal
def _reveal_cell(self, row: int, col: int) -> float:
"""Reveal the cell at (row, col). Returns the reward for the action."""
if (row, col) in self._revealed_cells or (row, col) in self._flags_placed:
return -0.05 # Penalty for revealing already revealed or flagged cell
if (row, col) in self._mine_positions:
self._game_status = GameStatus.LOST
self._revealed_cells.add((row, col))
return -10.0 # Penalty for hitting a mine
# Reveal the cell and potentially adjacent cells if count is 0
self._reveal_recursive(row, col)
return 1.0 # Small reward for safe reveal
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/envs/minesweeper_env/server/minesweeper_environment.py
Line: 180:195

Comment:
**logic:** cell added to `_revealed_cells` on line 185, then `_reveal_recursive` adds it again on line 207, causing redundant addition

```suggestion
    def _reveal_cell(self, row: int, col: int) -> float:
        """Reveal the cell at (row, col). Returns the reward for the action."""
        if (row, col) in self._revealed_cells or (row, col) in self._flags_placed:
            return -0.05  # Penalty for revealing already revealed or flagged cell

        if (row, col) in self._mine_positions:
            self._game_status = GameStatus.LOST
            self._revealed_cells.add((row, col))
            return -10.0  # Penalty for hitting a mine

        # Reveal the cell and potentially adjacent cells if count is 0
        self._reveal_recursive(row, col)

        return 1.0  # Small reward for safe reveal
```

How can I resolve this? If you propose a fix, please make it concise.

Comment thread tests/envs/test_minesweeper_env.py Outdated
Comment on lines +40 to +58
def test_setup_server(self):
"""Set up the Minesweeper server for testing."""
self.server_process = subprocess.Popen(
["python", "-m", "envs.minesweeper_env.server.app"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
# Give it a few seconds to start
time.sleep(3)

def check_server_running(self):
"""Check if the server is running and healthy."""
try:
response = requests.get("http://127.0.0.1:8000/health")
self.assertEqual(response.status_code, 200)
except requests.ConnectionError:
self.fail("Server did not start or is unreachable")

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.

logic: each test starts a new server but doesn't clean up previous ones, causing port conflicts. Tests should share one server instance across all tests using setUpClass and tearDownClass

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/envs/test_minesweeper_env.py
Line: 40:58

Comment:
**logic:** each test starts a new server but doesn't clean up previous ones, causing port conflicts. Tests should share one server instance across all tests using `setUpClass` and `tearDownClass`

How can I resolve this? If you propose a fix, please make it concise.

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s CMD curl -f http://localhost:8000/health || exit 1

# Run the FastAPI server
CMD ["/app/.venv/bin/uvicorn", "minesweeper.server.app:app", "--host", "0.0.0.0", "--port", "8000"]

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.

syntax: module path minesweeper.server.app:app is incorrect. Based on the COPY and PYTHONPATH setup, it should be server.app:app

Suggested change
CMD ["/app/.venv/bin/uvicorn", "minesweeper.server.app:app", "--host", "0.0.0.0", "--port", "8000"]
CMD ["/app/.venv/bin/uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "8000"]
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/envs/minesweeper_env/server/Dockerfile
Line: 67:67

Comment:
**syntax:** module path `minesweeper.server.app:app` is incorrect. Based on the COPY and PYTHONPATH setup, it should be `server.app:app`

```suggestion
CMD ["/app/.venv/bin/uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "8000"]
```

How can I resolve this? If you propose a fix, please make it concise.

@acharyaanusha acharyaanusha force-pushed the feature/minesweeper_env branch from 687084c to 12d90b8 Compare January 21, 2026 16:24
acharyaanusha and others added 5 commits January 21, 2026 08:25
Implement a Minesweeper game environment for reinforcement learning agents.
The environment features a grid-based game where agents must reveal all
non-mine cells without triggering mines.

Key features:
- 5x5 grid with configurable mine placement
- Two action types: reveal cells and place/remove flags
- Number indicators showing adjacent mine counts
- Recursive cell reveal for cells with zero adjacent mines
- Reward system based on game progress and outcomes
- Game status tracking (ONGOING, WON, LOST)

Components:
- Environment server with FastAPI integration
- Game logic in MinesweeperEnvironment class
- HTTP client for remote environment access
- Pydantic models for actions and observations
- Package configuration with dependencies
Add containerization and documentation for the Minesweeper environment
to enable easy deployment and usage.

Docker support:
- Multi-stage Dockerfile based on openenv-base image
- Build script for convenient local image creation
- Health check endpoint configuration
- Optimized layer caching for faster builds

Documentation:
- Comprehensive README with usage examples
- Quick start guide with code samples
- Environment details (actions, observations, rewards)
- Docker build and deployment instructions
- Configuration options and default settings

Example:
- Interactive agent demonstration script
- Shows environment lifecycle (reset, step, close)
- Demonstrates both reveal and flag actions
- Example output with game state visualization
Add comprehensive test suite and GitHub Actions workflow integration
for automated building and deployment of the Minesweeper environment.

Test suite (13 test cases):
- Server setup and health checks
- Initial state validation (board size, unrevealed cells)
- Action tests (reveal, flag, toggle flag)
- Edge cases (invalid positions, already revealed cells)
- Game state management and status tracking
- Board cell value validation
- Reset functionality and state cleanup
- Multi-step action sequences
- Proper handling of HTTP serialization for enums

CI/CD integration:
- Add minesweeper-env to GitHub Actions build matrix
- Automated Docker image building and publishing
- Multi-platform support (linux/amd64, linux/arm64)
- Image pushed to GitHub Container Registry
- Changed client from HTTPEnvClient to EnvClient (WebSocket)
- Updated all imports from openenv_core to openenv.core pattern
- Converted models from @DataClass to pure Pydantic with Field validation
- Fixed copy-paste documentation from echo_env template
- Simplified server/app.py to match new factory pattern
- Added missing newlines at end of files (POSIX compliance)
- Fixed Docker workflow merge conflict (removed duplicate entries)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@acharyaanusha acharyaanusha force-pushed the feature/minesweeper_env branch from 12d90b8 to 98bf1d4 Compare January 21, 2026 16:26
@acharyaanusha

acharyaanusha commented Jan 21, 2026

Copy link
Copy Markdown
Contributor Author

@Darktex updated based on greptile comments

@burtenshaw

Copy link
Copy Markdown
Collaborator

Looks good @acharyaanusha. Have you also deployed it to the HF hub, and updated the environments page

@acharyaanusha

Copy link
Copy Markdown
Contributor Author

Looks good @acharyaanusha. Have you also deployed it to the HF hub, and updated the environments page

@burtenshaw No I haven't but I could do it and let you know

acharyaanusha and others added 4 commits February 5, 2026 08:10
…acity errors

Tests were creating new MinesweeperEnv clients per test without closing them,
exhausting the server's single-session capacity. Added setUp/tearDown for
proper client lifecycle and use sys.executable for the server subprocess.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
acharyaanusha and others added 4 commits February 23, 2026 08:19
Use unittest.IsolatedAsyncioTestCase with async with context managers
to work directly with the async MinesweeperEnv client, avoiding the
SyncEnvClient wrapper. Each test properly connects and disconnects
within a single event loop, preventing session capacity errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…le immediately

Initialize _mine_counts with proper dimensions and call self.reset()
in __init__ to ensure the board is fully set up before any step call.
Prevents IndexError in _reveal_recursive when the web interface calls
step on a freshly constructed environment instance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@acharyaanusha

Copy link
Copy Markdown
Contributor Author

@acharyaanusha

Copy link
Copy Markdown
Contributor Author

@Darktex : ready for final review!

@acharyaanusha

Copy link
Copy Markdown
Contributor Author

@burtenshaw ready for another review

@Darktex Darktex 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.

Note: This is an automated review by Claude Code, not a human review.


Alignment Review Report

Automated Checks

  • Lint: SKIP (files are under src/envs/ not envs/; lint.sh scopes to src/ for usort and envs/ for ruff format, so these new files would land in neither scope correctly until the path issue is resolved)
  • Debug code: examples/minesweeper_agent.py uses print() throughout and build_docker.sh has decorative emoji echo lines — acceptable in scripts/examples, but worth noting both violate the project's no-emoji convention.

Tier 1: Fixes Required

BLOCKER

  • src/envs/minesweeper_env/server/minesweeper_environment.py:_check_win_conditionWin condition can overwrite LOST with WON. When a mine is revealed, _reveal_cell sets _game_status = GameStatus.LOST and adds the mine cell to _revealed_cells. Immediately after, step() unconditionally calls _check_win_condition(). That method checks len(self._revealed_cells) == total_cells - self.num_mines — but _revealed_cells now includes the mine cell. On a near-complete board (e.g., 20 safe cells with 19 already revealed), hitting the last mine yields revealed_count = 20 == 20, triggering GameStatus.WON and overwriting LOST. Fix: guard _check_win_condition to skip if the game is already over, or exclude mine cells from the revealed count.

  • .github/workflows/docker-build.yml:88-89Missing context key for minesweeper-env; the Dockerfile path is wrong and references a non-existent directory. The entry is:

    - name: minesweeper-env
      dockerfile: src/envs/minesweeper_env/server/Dockerfile

    No context field, and the Dockerfile expects src/envs/minesweeper_env to be COPY-able from the root. This is moot until the path issue below is resolved.

MAJOR

  • src/envs/minesweeper_env/server/minesweeper_environment.py:_reveal_cellPenalty returned for flagged cell is ambiguous. Returns -0.05 if the target cell is in _flags_placed and silently eats the action without toggling the flag — the agent gets no feedback that the cell is flagged. Per PATTERNS.md, return error info in observations.

  • src/envs/minesweeper_env/models.py / client.py / server/app.pytry/except ImportError blocks are identically duplicated in both branches. Example in client.py:

    try:
        from openenv.core.client_types import StepResult
    except ImportError:
        from openenv.core.client_types import StepResult  # identical line

    The except branch imports from the exact same package; only from .models import ... vs from models import ... differs. This will silently mask real ImportErrors from misspelled or missing modules.

  • src/envs/minesweeper_env/models.py:MinesweeperObservationboard: List[List[Any]] is not JSON-safe as specified. Board contains mixed int and str sentinels (-1, 0-8, 'F', '*'). The schema is untyped (Any), making auto-generated OpenAPI docs and JSON Schema validation meaningless. The test itself has assert isinstance(observation.game_status, (GameStatus, int)) — a signal that round-tripping through HTTP loses enum type information. Use a discriminated union or typed sentinel model.

  • src/envs/minesweeper_env/server/DockerfileBuild runs as root. No USER directive. Follow the pattern in envs/echo_env/server/Dockerfile.

MINOR

  • tests/envs/test_minesweeper_env.py:8-12sys.path.insert and os.environ["PYTHONPATH"] manipulation at module level duplicates what the test runner already provides (PYTHONPATH=src:envs). Remove.

  • tests/envs/test_minesweeper_env.py:test_reveal_already_revealed — Fragile branching. If (2,2) was a mine and the game ended, the second step returns reward=0.0 ("game already over"), not a negative penalty, causing a spurious assertion failure.

  • src/envs/minesweeper_env/server/minesweeper_environment.py:__init__ — Calls self.reset() in __init__. Anti-pattern: a subclass override of reset() referencing subclass attributes will fail. Require explicit reset() calls (Gym convention).


Tier 2: Alignment Discussion

ALIGNMENT FLAG — Wrong environment root directory

  • Principle at stake: PATTERNS.md canonical layout — every environment lives under envs/<name>/; all existing environments follow this.
  • Concern: This PR places the env at src/envs/minesweeper_env/. The src/ subtree is for openenv-core library code (src/openenv/). Placing an environment there conflates library code with environment definitions, breaks the docker build matrix (which uses envs/ paths for context), and is inconsistent with every other env in the repo. All 14 new files need to move to envs/minesweeper_env/.

ALIGNMENT FLAG — Missing typed generics on Environment

  • Principle at stake: INVARIANTS.md §API Invariants — "All environments must use Environment[ActT, ObsT, StateT] generics".
  • Concern: MinesweeperEnvironment(Environment) is declared without type parameters; should be Environment[MinesweeperAction, MinesweeperObservation, MinesweeperState]. The state property returns the base State rather than MinesweeperState.

ALIGNMENT FLAG — Simulation-control surface exposed

  • Principle at stake: INVARIANTS.md §Security Invariants — "Agents cannot access reset/simulation controls"; PRINCIPLES.md — "Rewards inside environment / Domain knowledge encapsulated".
  • Concern: get_full_state() exposes the complete mine layout (mine_locations, revealed_cells, flags). get_legal_actions() exposes the full legal action space. Neither is routed through app.py today, but they are public methods on an object accessible through the server. If a future MCP layer auto-exposes public methods, an agent could query mine positions. Make these private (_get_full_state) or remove.

Summary

3 blockers, 6 major/minor mechanical issues, 3 alignment flags.

Top priority before this can land:

  1. Move all 14 files from src/envs/minesweeper_env/ to envs/minesweeper_env/ to match the project convention; fix docker-build.yml context accordingly.
  2. Fix the win-condition bug (LOST overwritten by WON when the last mine is hit on a near-complete board).
  3. Add a non-root USER to the Dockerfile.
  4. Remove the no-op try/except ImportError shims.
  5. Type the board observation (no List[List[Any]]).

Thanks for the contribution — the test coverage is solid and the README is thorough. Once the directory placement and the win-condition bug are sorted, the rest is incremental polish.


Automated review by Claude Code | Learn more

@Darktex Darktex 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.

Note: This is an automated review by Claude Code, not a human review.


Alignment Review Report — Tier 1/Tier 2

Reviewed against .claude/docs/PRINCIPLES.md, .claude/docs/INVARIANTS.md, and the reference env at envs/echo_env/.

Automated Checks

  • Lint (eyeball): imports are sorted, formatting is consistent, no obvious style violations.
  • Debug code: many print(...) calls in examples/minesweeper_agent.py — these are intentional demo prints in an example script (outside src/), so they fall outside the check-debug.sh scope. No breakpoint(), pdb, or TODO/FIXME in production code.

Tier 1: Fixes Required

  • .github/workflows/docker-build.yml — the new minesweeper-env entry is missing its context: key. The context: envs/connect4_env line shown in the diff belongs to connect4_env. As-written, the Docker build for minesweeper will either reuse the previous context or fail entirely. Add an explicit context: pointing at the env directory (matching wherever the Dockerfile's COPY paths assume).
  • src/envs/minesweeper_env/server/app.py:17-22 — the try/except import is a no-op: both branches do from openenv.core.env_server import create_app. Remove the dead fallback.
  • src/envs/minesweeper_env/models.py:635-640 — same no-op try/except (both branches import from openenv.core.env_server.types). Remove.
  • src/envs/minesweeper_env/server/minesweeper_environment.py:1023-1030 — same no-op try/except for Environment / State. Remove.
  • src/envs/minesweeper_env/client.py:509-520 — the try/except claims to support a "standalone" install but both branches import from the absolute openenv.core.* path. If the intent is a fallback for users without the openenv package installed, the except branch needs from models import MinesweeperAction, MinesweeperObservation (or similar local-relative form). As written, the fallback can never succeed.
  • tests/envs/test_minesweeper_env.py:1351os.environ["PYTHONPATH"] = SRC_PATH mutates the process environment at import time, which both pollutes the rest of the test run and won't affect already-loaded modules. The sys.path.insert(...) on the preceding line is sufficient; drop the os.environ mutation.
  • src/envs/minesweeper_env/server/minesweeper_environment.py:1077self.reset() in __init__ couples construction with a stateful reset. This works when create_app treats the class as a per-session factory (which the comment at app.py:928 suggests it does), but it diverges from the Gymnasium convention that reset() is called explicitly by the orchestrator. Consider removing the implicit reset and relying on the harness to call reset().

Tier 2: Alignment Discussion

ALIGNMENT FLAG: Wrong location — env lives under src/envs/ instead of envs/

  • Principle at stake: "One canonical way to build environments" (PRINCIPLES.md)
  • The concern: Every existing environment (echo_env, connect4_env, chess_env, git_env, …) lives at the repo root under envs/. This PR places the new env at src/envs/minesweeper_env/. The README import examples in the PR even use from envs.minesweeper_env import ..., suggesting the intended path is envs/. The CI workflow diff confirms the mismatch (Dockerfile context inconsistency, above).
  • Suggested reviewer: @Darktex

ALIGNMENT FLAG: reward is part of the observation payload

  • Principle at stake: Gymnasium-compatible step contract; rewards are a separate channel, not an observation feature.
  • The concern: MinesweeperObservation carries a reward field (and client._parse_result reads reward from both payload["reward"] and obs_data["reward"]). This conflates the observation space with the reward signal and gives the agent a feature it should not be conditioning on. Reward belongs alongside done in the step return, not inside the observation.
  • Suggested reviewer: @Darktex

ALIGNMENT FLAG: MinesweeperState exposes hidden ground truth on a publicly-importable model

  • Principle at stake: Agent isolation (INVARIANTS.md §"Agents cannot reset / cannot peek")
  • The concern: MinesweeperEnvironment.get_full_state() returns the full MinesweeperState, which includes mine_locations. MinesweeperState.to_observation() is also defined on the model class. There is no HTTP/MCP route exposing this today, but having it on the publicly-importable class is a latent risk — a future contributor wiring up "introspection" endpoints could trivially leak the solution. Recommend moving the full-state object behind a server-private boundary and only exporting the redacted to_observation() shape.
  • Suggested reviewer: @Darktex

ALIGNMENT FLAG: No MCP tool layer

  • Principle at stake: "MCP as universal standard" (RFC 003), dual-API boundary (INVARIANTS.md)
  • The concern: The env only exposes the HTTP/WebSocket server (app.py); there are no MCP tool definitions like other reference envs include. Per RFC 003, agent-environment interaction is supposed to flow through MCP tools while the WebSocket layer is for training orchestration. This env skips the agent-facing half of the API boundary.
  • Suggested reviewer: @Darktex

Summary

  • 7 mechanical issues to fix (the missing CI context: key is the most urgent — it will break Docker builds).
  • 4 alignment points that need human discussion before this is the canonical "how to add an env" example.

Verdict: request_changes


Automated review by Claude Code | Learn more

- docker-build.yml: set explicit context: . for minesweeper-env (Dockerfile
  COPY paths assume repo root)
- Remove duplicate try/except ImportError blocks where both branches imported
  the same path (no-op fallback) in app.py, models.py, minesweeper_environment.py
- client.py: only the models import differs between in-repo/standalone, so
  flatten core imports and keep relative/absolute fallback only for models
- Drop os.environ["PYTHONPATH"] mutation and redundant sys.path.insert in
  tests; PYTHONPATH=src:envs is provided by the test runner
- Remove implicit self.reset() in MinesweeperEnvironment.__init__; rely on
  explicit reset() per Gymnasium convention
Tier 2 alignment fixes from PR review:

- Move src/envs/minesweeper_env/ -> envs/minesweeper_env/ so the env lives
  alongside every other environment in the repo (echo_env, connect4_env, etc.)
- Rewrite Dockerfile to match the connect4/echo pattern: build context is the
  env directory and uses 'uv sync' rather than copying repo-root core sources
- Update docker-build.yml to set the new path + 'context: envs/minesweeper_env'
- Tidy pyproject.toml to match the connect4_env layout (package name
  minesweeper_env, script entry, package-dir mapping)
- README: update docker build invocation to the new path
- Make MinesweeperEnvironment.get_full_state private (_get_full_state) since
  it exposes mine locations and must never be reachable by an agent
@acharyaanusha acharyaanusha requested a review from Darktex May 14, 2026 04:02
- Guard _check_win_condition against overwriting LOST with WON when a
  mine reveal pushes total revealed_count to the win threshold; count
  only non-mine cells.
- Type MinesweeperEnvironment generics (Environment[Action, Obs, State]);
  keep public state as base State so /state cannot leak mine_positions.
- Surface action-rejection reasons (already revealed / flagged / can't
  flag a revealed cell) in observation.metadata["error"].
- De-flake test_reveal_already_revealed (handle the mine-on-first-reveal
  branch by re-trying candidates instead of asserting on game-over reward).
- Add direct unit tests for the win-condition guard and metadata errors.
@acharyaanusha

Copy link
Copy Markdown
Contributor Author

@Darktex hey addressed all the comments. ready for re-review

@burtenshaw burtenshaw added the size: large Large pull request label Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot. New Environment size: large Large pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants