# Managed Environment Pools

> Managed pools provide a "data in, optimized pipeline out" experience: users upload files (or point to a GitHub repo / S3 bucket), an LLM agent analyzes the data, and the system assembles a fully configured environment…

- Skill: `tools-only/managed-environment-pools` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add tools-only/managed-environment-pools`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tools-only/managed-environment-pools/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tools-only (https://skillmd.com/u/tools-only)
- Updated: 2026-09-29
- Page: https://skillmd.com/skills/tools-only/managed-environment-pools

---

# Managed Environment Pools

Managed pools provide a "data in, optimized pipeline out" experience: users upload
files (or point to a GitHub repo / S3 bucket), an LLM agent analyzes the data, and
the system assembles a fully configured environment pool ready for GEPA/Eval.

**Owner:** `rust_backend/src/managed_env_pool/`
**Agent execution:** `sandbox-agent` (wraps OpenCode CLI into HTTP API)
**Pool primitives:** `rhodes-core` crate

## Architecture

```
User (SDK / CLI / Dashboard)
        |
        | file upload / GitHub URL / S3 config
        v
+---------------------------+
|   Synth Backend (Python)  |   auth, billing, org scoping
+------------+--------------+
             |
             v
+--------------------------------------------------+
|  rust_backend/src/managed_env_pool/              |
|                                                  |
|  1. Data Ingestion                               |
|     FileIngestor | GitIngestor | S3Ingestor      |
|     -> DataManifest (inventory, hashes, metadata) |
|                                                  |
|  2. Assembly Engine                              |
|     sandbox-agent HTTP -> OpenCode -> Claude LLM |
|     Fallback: opencode CLI -> heuristic          |
|     -> AssemblyResult (env type, files, config)  |
|                                                  |
|  3. Pool Provisioner                             |
|     PoolConfig + generated files -> PoolRegistry |
+--------------------------------------------------+
             |
             v
+--------------------------------------------------+
|  rhodes-core (imported crate)                    |
|  PoolConfig, PoolRegistry, SandboxAgentClient    |
+--------------------------------------------------+
```

## Data Flow

1. **Ingest** -- user provides data via upload, GitHub URL, or S3 reference.
   A `DataManifest` is produced (file listing with sizes, SHA256 hashes, MIME types).

2. **Assemble** -- the assembly engine sends the manifest plus sample file
   contents to an LLM agent (Claude via OpenCode via sandbox-agent). The agent
   selects an environment type (harbor, openenv, browser, archipelago) and
   generates configuration files (task.toml, Dockerfile, localapi.py, etc.).

3. **Provision** -- the assembly output is converted to a `PoolConfig`, generated
   files are stored in blob storage, and the pool is registered with the pool
   registry.

## API Endpoints

All endpoints are org-scoped via API key (`x-user-api-key` header).

### Data Sources

| Method | Path | Description |
|--------|------|-------------|
| POST | `/v1/managed-pools/uploads` | Generate presigned upload URL |
| POST | `/v1/managed-pools/data-sources` | Create data source (multipart upload, GitHub, or S3) |
| GET | `/v1/managed-pools/data-sources` | List data sources for org |
| GET | `/v1/managed-pools/data-sources/:id` | Get data source details |
| PUT | `/v1/managed-pools/data-sources/:id` | Update data source (re-upload or change git ref) |
| DELETE | `/v1/managed-pools/data-sources/:id` | Delete data source |
| GET | `/v1/managed-pools/data-sources/:id/manifest` | Get parsed data manifest |
| POST | `/v1/managed-pools/data-sources/:id/refresh` | Re-fetch from GitHub/S3 and regenerate manifest |

### Assembly

| Method | Path | Description |
|--------|------|-------------|
| POST | `/v1/managed-pools/assemble` | Start assembly from data source |
| GET | `/v1/managed-pools/assembly/:id` | Get assembly status and result |
| GET | `/v1/managed-pools/assembly/:id/generated` | Get generated files from completed assembly |
| GET | `/v1/managed-pools/assembly/:id/events` | SSE stream of assembly lifecycle events |

**Assembly request body:**
```json
{
  "data_source_id": "ds_...",
  "exclusion_patterns": ["*.log", "node_modules/**"],
  "target_hint": "openenv",
  "agent_model": "claude-sonnet-4-20250514"
}
```

### Managed Pools

| Method | Path | Description |
|--------|------|-------------|
| POST | `/v1/managed-pools` | Create pool from completed assembly |
| GET | `/v1/managed-pools` | List pools for org |
| GET | `/v1/managed-pools/:id` | Get pool details |
| DELETE | `/v1/managed-pools/:id` | Delete pool |
| GET | `/v1/managed-pools/:id/status` | Get pool status |
| GET | `/v1/managed-pools/:id/generated` | Get generated files |
| PUT | `/v1/managed-pools/:id/exclusions` | Update file exclusion patterns |
| POST | `/v1/managed-pools/:id/re-assemble` | Trigger re-assembly |

## Assembly Engine

The assembly engine has a 3-tier fallback:

1. **sandbox-agent HTTP** (preferred) -- creates a session on the sandbox-agent
   server, sends the prompt to OpenCode which calls the Claude LLM, polls
   universal-schema events via HTTP, collects text deltas, and parses the
   structured JSON output.

2. **opencode CLI** -- spawns OpenCode as a subprocess with `--format json`,
   pipes the prompt via stdin, and parses JSONL events from stdout.

3. **heuristic** -- pattern-matches on file names and content to guess the
   environment type and generate scaffold files without an LLM.

### sandbox-agent Integration

The sandbox-agent is a Rust HTTP server that wraps coding agents (Claude Code,
OpenCode, Codex) into a unified session-based API with universal event schema.

**Session lifecycle (from rust_backend perspective):**

```
1. POST /v1/sessions/{id}          -- create session (agent=opencode, model, permissionMode)
2. POST /v1/sessions/{id}/messages -- send assembly prompt
3. GET  /v1/sessions/{id}/events   -- poll universal events (offset pagination)
4. POST /v1/sessions/{id}/permissions/{pid}/reply -- auto-approve permissions
5. POST /v1/sessions/{id}/terminate -- cleanup
```

**Event processing:** The engine collects `item.delta` events for text content
and watches for `session.idle` or `session.ended` to detect completion.
`permission.requested` events are auto-approved with "always".

**OpenCode SSE specifics:** OpenCode v1.1.18+ wraps SSE events in a
`{"directory":"...","payload":{...}}` structure. The sandbox-agent unwraps the
`payload` before converting to universal schema. A typed dispatch function
(`opencode_typed_event`) routes events by the `type` field to avoid issues with
the generated `#[serde(untagged)]` Event enum where greedy variants swallow
events.

### Agent Output Format

The agent is prompted to output structured JSON:

```json
{
  "environment_type": "openenv",
  "reasoning": "This is a text classification task...",
  "generated_files": {
    "task.toml": "...",
    "localapi.py": "...",
    "Dockerfile": "..."
  },
  "pool_config": {
    "pool_id": "...",
    "type": "openenv",
    "concurrency": 4
  },
  "task_definitions": [...]
}
```

The parser is lenient: `pool_config` and `task_definitions` are optional with
fallback defaults. The JSON extractor finds the last valid JSON block in the
agent output to avoid capturing prompt echo text.

## Environment Types

| Type | Pool Backend | Use Case | Required Files |
|------|-------------|----------|----------------|
| `openenv` | OpenEnv | Classification, RL, sequential decision tasks | `localapi.py`, `task.toml`, `Dockerfile` |
| `harbor` | Sandbox | Code execution with test suites | `task.toml`, `tests/test.sh`, `instruction.md` |
| `browser` | Browser/Kernel | Web automation tasks | `browser_task.yaml` |
| `archipelago` | Archipelago | Multi-step tasks with MCP tools | `mcp_config.json` |

## Feature Flags

| Environment Variable | Default | Description |
|---------------------|---------|-------------|
| `MANAGED_POOLS` | `true` | Enable managed pools feature |
| `MANAGED_POOLS_GITHUB` | `false` | Enable GitHub data sources |
| `MANAGED_POOLS_S3` | `false` | Enable S3 data sources |
| `MANAGED_POOLS_AUTO_REASSEMBLY` | `false` | Auto-reassemble on data/exclusion changes |
| `MANAGED_POOLS_SANDBOX_AGENT_URL` | (none) | sandbox-agent server URL (enables agent path) |
| `MANAGED_POOLS_ASSEMBLY_AGENT` | `opencode` | Agent to use for assembly |
| `MANAGED_POOLS_ASSEMBLY_MODEL` | (none) | Default model for assembly agent |
| `MANAGED_POOLS_SANDBOX_PERMISSION_MODE` | `bypass` | Permission mode for agent sessions |
| `MANAGED_POOLS_ASSEMBLY_TIMEOUT_SEC` | `600` | Assembly timeout in seconds |

## Assembly Events (SSE)

Events streamed via `GET /v1/managed-pools/assembly/:id/events`:

| Event Type | Description |
|-----------|-------------|
| `assembly.started` | Assembly job created |
| `assembly.agent_spawned` | Agent session created |
| `assembly.agent_failed` | Agent path failed (falling back) |
| `assembly.files_generated` | Generated files stored |
| `assembly.completed` | Assembly finished successfully |
| `assembly.failed` | Assembly failed with error |

## Directory Structure

```
rust_backend/src/managed_env_pool/
├── mod.rs              # ManagedPoolService, feature flags, orchestration
├── types.rs            # DataSource, AssemblyRecord, ManagedPool, etc.
├── upload.rs           # Presigned upload URL generation
├── api/
│   ├── mod.rs
│   ├── routes.rs       # 21 Axum route handlers
│   ├── requests.rs     # Request DTOs
│   └── responses.rs    # Response DTOs
├── assembly/
│   ├── mod.rs
│   ├── engine.rs       # 3-tier assembly engine (agent -> CLI -> heuristic)
│   ├── prompts.rs      # LLM prompt template
│   ├── parser.rs       # Lenient JSON parser for agent output
│   └── generators/     # Heuristic fallback generators
│       ├── openenv.rs  # OpenEnv localapi.py generation
│       ├── harbor.rs   # Harbor scaffold
│       ├── browser.rs  # Browser scaffold
│       └── archipelago.rs
├── ingestion/
│   ├── mod.rs
│   ├── file.rs         # File/archive ingestor
│   ├── github.rs       # Git clone ingestor
│   ├── s3.rs           # S3 bucket ingestor
│   └── manifest.rs     # DataManifest builder
├── provisioner/
│   ├── mod.rs
│   └── storage.rs      # Generated file blob storage
└── store/
    ├── mod.rs
    └── postgres.rs     # DB persistence for data sources, assemblies, pools
```

## MVP: Banking77 Flow

1. User uploads `test.csv` + `categories.json` (Banking77 dataset)
2. System ingests files and produces `DataManifest`
3. User calls `POST /v1/managed-pools/assemble`
4. Assembly engine sends manifest + file samples to Claude via sandbox-agent
5. Agent determines: environment_type = openenv, generates localapi.py with
   text classification logic, Dockerfile, task.toml
6. Pool is provisioned as PoolType::Openenv with concurrency=4
7. User runs GEPA against the managed pool

