# Open Model Gym Run agent tests across a matrix of **models × runners × scenarios**. It isn't hard for any agent to do ok with opus, but lets scale things in the other direction. What do we have to break things down to. image ## Quick Start ```bash just install # one-time setup just run # run full matrix (3 reps each) just report # view results ``` ## How It Works The test harness runs every combination of models, runners, and scenarios defined in your matrix. Each test runs multiple times (default 3) and keeps the **worst result** — if a test fails even once, it's marked failed. This catches flaky passes. ## Configuration Edit `config.yaml` to define your test matrix: ### Models LLMs to test against. Supports any provider (Anthropic, OpenAI, Ollama, etc.): ```yaml models: - name: opus provider: anthropic model: claude-opus-4-5-20251101 - name: qwen3-coder provider: ollama model: qwen3-coder:64k - name: gpt4 provider: openai model: gpt-4-turbo ``` ### Runners Agent frameworks that execute the tests. Each runner has its own binary, type, and configuration: ```yaml runners: # Goose agent with extensions - name: goose-full type: goose bin: goose # path to binary (can be absolute) extensions: [developer, todo, skills] stdio: - node mcp-harness/dist/index.js # OpenCode agent - name: opencode type: opencode bin: opencode # path to binary stdio: - node mcp-harness/dist/index.js # Custom goose binary path - name: goose-dev type: goose bin: /path/to/my/goose-dev extensions: [developer] ``` **Supported runner types:** - `goose` — [Goose](https://github.com/aaif-goose/goose) agent framework - `opencode` — [OpenCode](https://opencode.ai) agent framework - `pi` — [Pi](https://github.com/badlogic/pi-mono) coding agent ## Runner Details Each runner has different setup requirements, MCP integration methods, and session handling. ### Goose [Goose](https://github.com/aaif-goose/goose) is an open-source coding agent with built-in MCP support. **Setup:** Install via `brew install goose` or from source. **MCP Integration:** Native support. The harness writes a `config.yaml` to an isolated `.goose-root/` directory with extensions and MCP servers: ```yaml extensions: developer: enabled: true mcp_harness: type: stdio enabled: true cmd: node args: [mcp-harness/dist/index.js] ``` **Session Handling:** Uses `--name ` for named sessions, `--resume` to continue: - Turn 1: `goose run -i --name ` - Turn 2+: `goose run -i --name --resume` - Single-turn: `goose run -i --no-session` ### OpenCode [OpenCode](https://opencode.ai) is a terminal-based coding agent. **Setup:** Install via their website or package manager. **MCP Integration:** Native support. The harness writes an `opencode.json` config to the workdir: ```json { "mcp": { "harness": { "type": "local", "command": ["node", "mcp-harness/dist/index.js"], "enabled": true } }, "model": "anthropic/claude-opus-4-5-20251101" } ``` **Session Handling:** Uses `--continue` to resume the last session in the working directory: - Turn 1: `opencode run ""` - Turn 2+: `opencode run --continue ""` ⚠️ OpenCode doesn't support named sessions, so multi-turn scenarios exclude it. ### Pi [Pi](https://github.com/badlogic/pi-mono) is a lightweight coding agent that requires an adapter for MCP support. **Setup:** ```bash # Install Pi npm install -g @anthropic/pi # or from source # Install the MCP adapter (required for MCP tools) pi install npm:pi-mcp-adapter ``` The `just install` recipe auto-installs pi-mcp-adapter if missing. **MCP Integration:** Via [pi-mcp-adapter](https://github.com/nicobailon/pi-mcp-adapter). The harness dynamically writes a `.pi-mcp.json` config to the workdir: ```json { "mcpServers": { "harness": { "command": "node", "args": ["mcp-harness/dist/index.js"], "lifecycle": "eager", "env": { "MCP_HARNESS_LOG": "/tool-calls.log" } } }, "settings": { "directTools": true } } ``` Key settings: - `directTools: true` — Registers MCP tools directly in Pi's tool list (no wrapper) - `lifecycle: "eager"` — Connects to MCP servers at startup **Model Configuration:** Pi requires custom models (like Ollama) to be defined in `models.json`. The harness automatically generates this config in an isolated `.pi-root/` directory and sets `PI_CODING_AGENT_DIR` to use it: ```json { "providers": { "ollama": { "baseUrl": "http://localhost:11434/v1", "api": "openai-completions", "apiKey": "ollama", "models": [{ "id": "model-name", "name": "Model Name", ... }] } } } ``` The harness copies `auth.json` from your real Pi config (`~/.pi/agent/`) so API keys work. **Session Handling:** Uses `--session ` for file-based sessions, `--continue` to resume: - Turn 1: `pi -p --session ""` - Turn 2+: `pi -p --continue --session ""` - Single-turn: `pi -p --no-session ""` The `-p` flag runs Pi in non-interactive "print" mode for automation ### Matrix Define which scenarios run against which models/runners: ```yaml matrix: - scenario: file-editing models: [opus, qwen3-coder] # omit to run all models runners: [goose-full, opencode] # omit to run all runners - scenario: everyday-app-automation # runs against ALL models and ALL runners ``` ## Scenarios Scenarios live in `suite/scenarios/` as YAML files: ```yaml name: file-editing description: Create and edit files prompt: | 1. Create joke.md containing a short joke 2. Edit hello.rs to add a debug function setup: hello.rs: | fn main() { println!("Hello!"); } validate: - type: file_exists path: joke.md - type: file_matches path: hello.rs regex: "fn\\s+debug" ``` ### Validation Rules | Rule | Description | |------|-------------| | `file_exists` | File exists at path | | `file_not_empty` | File exists and has content | | `file_contains` | File contains literal string | | `file_matches` | File matches regex pattern | | `command_succeeds` | Shell command exits 0 | | `tool_called` | MCP tool was called with matching args (regex supported) | **Tool call validation example:** ```yaml validate: - type: tool_called tool: slack_search_messages args: query: /quarterly.?review/ # regex pattern - type: tool_called tool: jira_create_issue args: summary: /Q1.*Review/ description: /David Brown/ ``` ## MCP Harness Mock MCP server providing simulated tools for testing agent tool-use without hitting real APIs. ```bash cd mcp-harness && npm install && npm run build ``` **Available tools:** gdrive, sheets, salesforce, slack, calendar, gmail, jira, github Each tool returns realistic mock data. Tool calls are logged to `tool-calls.log` in the workdir for validation. ## Commands | Command | Description | |---------|-------------| | `just run` | Full test run (3 reps each, worst kept) | | `just test` | Quick run (1 rep each) | | `just scenario ` | Run specific scenario | | `just agent ` | Run specific agent | | `just report` | Open HTML results | ### CLI Flags ```bash # Filter by scenario, model, or runner npx tsx src/runner.ts --scenario=file-editing --model=opus --runner=goose # Control repetition count npx tsx src/runner.ts --run-count=5 # Don't auto-open browser npx tsx src/runner.ts --no-open ``` ## Output - `report.html` — Live-updating HTML matrix showing pass/fail status, duration, and validation details - `logs/` — Full agent output logs for each run