From caac7bdbb2dbd7084f77b487efd83e4dbf14e746 Mon Sep 17 00:00:00 2001 From: Jaco du Preez Date: Wed, 4 Nov 2026 12:17:59 +0200 Subject: [PATCH 01/14] fix(config): load OpenCode MCP and permissions under the V2 schema OpenCode V2 reads `mcp.servers`, an ordered `permissions` array, and `commands`. The V1 shapes (`mcp` with direct children, `enabled`, a `permission` object, `command`) load without error and are then ignored. Under V2 the repo's MCP servers and deny rules were not in effect, including the deny rules for `rm -rf`, force pushes, and `sudo`. - opencode.json: move MCP servers under `mcp.servers` using `disabled`; rename `command` to `commands`; translate the permission rules to the ordered array. `plugins` is unchanged. - install.mjs: merge and remove MCP fragments under `mcp.servers`. - art and setup-engine MCP instructions: use the V2 shape. Verified in a scratch copy on OpenCode 2.2.5: under the migrated config `git reset --hard HEAD` returns Permission denied. Under the previous V1 config the same command was not blocked. Refs: #107 --- .agents/modules/art/mcp/aseprite.json | 2 +- .../modules/core/skills/setup-engine/SKILL.md | 10 +- .agents/skills/setup-engine/SKILL.md | 10 +- .opencode/modules/art/mcp/aseprite.json | 2 +- .../modules/core/skills/setup-engine/SKILL.md | 10 +- .opencode/modules/install.mjs | 14 +-- opencode.json | 96 +++++++++---------- 7 files changed, 73 insertions(+), 71 deletions(-) diff --git a/.agents/modules/art/mcp/aseprite.json b/.agents/modules/art/mcp/aseprite.json index 892a08f..fc8d117 100644 --- a/.agents/modules/art/mcp/aseprite.json +++ b/.agents/modules/art/mcp/aseprite.json @@ -9,7 +9,7 @@ "-m", "aseprite_mcp" ], - "enabled": true, + "codemode": false, "environment": { "ASEPRITE_PATH": "{env:ASEPRITE_PATH}" } diff --git a/.agents/modules/core/skills/setup-engine/SKILL.md b/.agents/modules/core/skills/setup-engine/SKILL.md index 64a5665..88a0d5c 100644 --- a/.agents/modules/core/skills/setup-engine/SKILL.md +++ b/.agents/modules/core/skills/setup-engine/SKILL.md @@ -470,23 +470,23 @@ After the package imports, MCP for Unity opens a **setup wizard** automatically: #### Manual MCP config (reliable fallback) -Add this block to the `mcp` object in your harness config (`opencode.json` for OpenCode, `pi.json` for Pi): +Add this block to `mcp.servers` in your harness config (`opencode.json` for OpenCode, `pi.json` for Pi): ```json "unity": { - "type": "local", + "type": "remote", "url": "http://localhost:8080/mcp", - "enabled": false + "disabled": true } ``` -`enabled: false` is the default. Flip to `true` once Unity Editor is running and you want OCGS agents to use unity-mcp tools. +`disabled: true` keeps it off (servers connect automatically otherwise). Set `disabled` to `false` once Unity Editor is running and you want OCGS agents to use unity-mcp tools. > ⚠ **Unity Editor must be running** before OCGS agents can use unity-mcp tools. If the Editor is closed, MCP calls will fail with a connection error. Open your project in Unity, then continue. #### Verify -In a session where `enabled: true`, call any unity-mcp tool (e.g. `list_scenes`) via OCGS. Expect a list of scenes from your open Unity project. +In a session where `disabled` is `false`, call any unity-mcp tool (e.g. `list_scenes`) via OCGS. Expect a list of scenes from your open Unity project. #### Troubleshooting diff --git a/.agents/skills/setup-engine/SKILL.md b/.agents/skills/setup-engine/SKILL.md index 7ff52be..886de71 100644 --- a/.agents/skills/setup-engine/SKILL.md +++ b/.agents/skills/setup-engine/SKILL.md @@ -790,23 +790,23 @@ After the package imports, MCP for Unity opens a **setup wizard** automatically: #### Manual MCP config (reliable fallback) -Add this block to the `mcp` object in your harness config (`opencode.json` for OpenCode, `pi.json` for Pi): +Add this block to `mcp.servers` in your harness config (`opencode.json` for OpenCode, `pi.json` for Pi): ```json "unity": { - "type": "local", + "type": "remote", "url": "http://localhost:8080/mcp", - "enabled": false + "disabled": true } ``` -`enabled: false` is the default. Flip to `true` once Unity Editor is running and you want OCGS agents to use unity-mcp tools. +`disabled: true` keeps it off (servers connect automatically otherwise). Set `disabled` to `false` once Unity Editor is running and you want OCGS agents to use unity-mcp tools. > ⚠ **Unity Editor must be running** before OCGS agents can use unity-mcp tools. If the Editor is closed, MCP calls will fail with a connection error. Open your project in Unity, then continue. #### Verify -In a session where `enabled: true`, call any unity-mcp tool (e.g. `list_scenes`) via OCGS. Expect a list of scenes from your open Unity project. +In a session where `disabled` is `false`, call any unity-mcp tool (e.g. `list_scenes`) via OCGS. Expect a list of scenes from your open Unity project. #### Troubleshooting diff --git a/.opencode/modules/art/mcp/aseprite.json b/.opencode/modules/art/mcp/aseprite.json index 892a08f..fc8d117 100644 --- a/.opencode/modules/art/mcp/aseprite.json +++ b/.opencode/modules/art/mcp/aseprite.json @@ -9,7 +9,7 @@ "-m", "aseprite_mcp" ], - "enabled": true, + "codemode": false, "environment": { "ASEPRITE_PATH": "{env:ASEPRITE_PATH}" } diff --git a/.opencode/modules/core/skills/setup-engine/SKILL.md b/.opencode/modules/core/skills/setup-engine/SKILL.md index 64a5665..88a0d5c 100644 --- a/.opencode/modules/core/skills/setup-engine/SKILL.md +++ b/.opencode/modules/core/skills/setup-engine/SKILL.md @@ -470,23 +470,23 @@ After the package imports, MCP for Unity opens a **setup wizard** automatically: #### Manual MCP config (reliable fallback) -Add this block to the `mcp` object in your harness config (`opencode.json` for OpenCode, `pi.json` for Pi): +Add this block to `mcp.servers` in your harness config (`opencode.json` for OpenCode, `pi.json` for Pi): ```json "unity": { - "type": "local", + "type": "remote", "url": "http://localhost:8080/mcp", - "enabled": false + "disabled": true } ``` -`enabled: false` is the default. Flip to `true` once Unity Editor is running and you want OCGS agents to use unity-mcp tools. +`disabled: true` keeps it off (servers connect automatically otherwise). Set `disabled` to `false` once Unity Editor is running and you want OCGS agents to use unity-mcp tools. > ⚠ **Unity Editor must be running** before OCGS agents can use unity-mcp tools. If the Editor is closed, MCP calls will fail with a connection error. Open your project in Unity, then continue. #### Verify -In a session where `enabled: true`, call any unity-mcp tool (e.g. `list_scenes`) via OCGS. Expect a list of scenes from your open Unity project. +In a session where `disabled` is `false`, call any unity-mcp tool (e.g. `list_scenes`) via OCGS. Expect a list of scenes from your open Unity project. #### Troubleshooting diff --git a/.opencode/modules/install.mjs b/.opencode/modules/install.mjs index 5cee95b..5c5394e 100644 --- a/.opencode/modules/install.mjs +++ b/.opencode/modules/install.mjs @@ -204,13 +204,14 @@ function cmdAdd(args) { } const ocfg = readJSON(OPENCODE_JSON) || {}; if (!ocfg.mcp) ocfg.mcp = {}; + if (!ocfg.mcp.servers) ocfg.mcp.servers = {}; let merged = false; for (const serverName of Object.keys(fragment)) { - if (ocfg.mcp[serverName]) { + if (ocfg.mcp.servers[serverName]) { log('WARN', `MCP server '${serverName}' already exists in opencode.json — skipped`); summary.mcpSkipped++; } else { - ocfg.mcp[serverName] = fragment[serverName]; + ocfg.mcp.servers[serverName] = fragment[serverName]; log('ADD', `MCP server '${serverName}' merged into opencode.json`); mcpServers.push(serverName); summary.mcpAdded++; @@ -376,14 +377,15 @@ function cmdRemove(name) { // Remove MCP servers from opencode.json (OpenCode-only) if (mcpServers.length > 0 && HARNESS !== 'pi') { const ocfg = readJSON(OPENCODE_JSON); - if (ocfg && ocfg.mcp) { + if (ocfg && ocfg.mcp && ocfg.mcp.servers) { for (const serverName of mcpServers) { - if (ocfg.mcp[serverName]) { - delete ocfg.mcp[serverName]; + if (ocfg.mcp.servers[serverName]) { + delete ocfg.mcp.servers[serverName]; log('DEL', `MCP server '${serverName}' from opencode.json`); } } - // Clean up empty mcp section + // Clean up empty servers and mcp sections + if (Object.keys(ocfg.mcp.servers).length === 0) delete ocfg.mcp.servers; if (Object.keys(ocfg.mcp).length === 0) delete ocfg.mcp; writeJSON(OPENCODE_JSON, ocfg); } diff --git a/opencode.json b/opencode.json index 9d5e671..3a51799 100644 --- a/opencode.json +++ b/opencode.json @@ -5,7 +5,7 @@ "./.opencode/plugins/drift-detector.ts", "./.opencode/plugins/changelog-generator.ts" ], - "command": { + "commands": { "hybrid-prototype": { "template": "Run the hybrid-prototype skill: load .agents/skills/hybrid-prototype/SKILL.md and follow the fast-lane prototype workflow. Concept: $ARGUMENTS", "description": "Fast-lane prototype for discovery phase — build a playable prototype in 2-3 days with minimal process overhead.", @@ -13,54 +13,54 @@ } }, "mcp": { - "aseprite": { - "type": "local", - "command": ["uv", "--directory", "tools/aseprite-mcp", "run", "-m", "aseprite_mcp"], - "enabled": true, - "environment": { - "ASEPRITE_PATH": "{env:ASEPRITE_PATH}" + "servers": { + "aseprite": { + "type": "local", + "command": ["uv", "--directory", "tools/aseprite-mcp", "run", "-m", "aseprite_mcp"], + "codemode": false, + "environment": { + "ASEPRITE_PATH": "{env:ASEPRITE_PATH}" + } + }, + "godot": { + "type": "local", + "command": ["npx", "@coding-solo/godot-mcp"], + "disabled": true, + "codemode": false, + "environment": { + "GODOT_PATH": "{env:GODOT_PATH}", + "DEBUG": "{env:DEBUG}" + } + }, + "unity": { + "type": "remote", + "url": "http://localhost:8080/mcp", + "disabled": true, + "codemode": false } - }, - "godot": { - "type": "local", - "command": ["npx", "@coding-solo/godot-mcp"], - "enabled": false, - "environment": { - "GODOT_PATH": "{env:GODOT_PATH}", - "DEBUG": "{env:DEBUG}" - } - }, - "unity": { - "type": "local", - "url": "http://localhost:8080/mcp", - "enabled": false } }, - "permission": { - "bash": { - "git status*": "allow", - "git diff*": "allow", - "git log*": "allow", - "git branch*": "allow", - "git rev-parse*": "allow", - "ls *": "allow", - "dir *": "allow", - "python -m json.tool*": "allow", - "python -m pytest*": "allow", - "py -m pytest*": "allow", - "rm -rf *": "deny", - "git push --force*": "deny", - "git push -f *": "deny", - "git reset --hard*": "deny", - "git clean -f*": "deny", - "sudo *": "deny", - "chmod 777*": "deny", - "*>.env*": "deny", - "cat *.env*": "deny", - "type *.env*": "deny" - }, - "read": { - "**/.env*": "deny" - } - } + "permissions": [ + { "action": "shell", "resource": "git status *", "effect": "allow" }, + { "action": "shell", "resource": "git diff *", "effect": "allow" }, + { "action": "shell", "resource": "git log *", "effect": "allow" }, + { "action": "shell", "resource": "git branch *", "effect": "allow" }, + { "action": "shell", "resource": "git rev-parse *", "effect": "allow" }, + { "action": "shell", "resource": "ls *", "effect": "allow" }, + { "action": "shell", "resource": "dir *", "effect": "allow" }, + { "action": "shell", "resource": "python -m json.tool *", "effect": "allow" }, + { "action": "shell", "resource": "python -m pytest *", "effect": "allow" }, + { "action": "shell", "resource": "py -m pytest *", "effect": "allow" }, + { "action": "shell", "resource": "rm -rf *", "effect": "deny" }, + { "action": "shell", "resource": "git push --force *", "effect": "deny" }, + { "action": "shell", "resource": "git push -f *", "effect": "deny" }, + { "action": "shell", "resource": "git reset --hard *", "effect": "deny" }, + { "action": "shell", "resource": "git clean -f *", "effect": "deny" }, + { "action": "shell", "resource": "sudo *", "effect": "deny" }, + { "action": "shell", "resource": "chmod 777 *", "effect": "deny" }, + { "action": "shell", "resource": "*>.env*", "effect": "deny" }, + { "action": "shell", "resource": "cat *.env*", "effect": "deny" }, + { "action": "shell", "resource": "type *.env*", "effect": "deny" }, + { "action": "read", "resource": "*.env*", "effect": "deny" } + ] } From b43fbab59a6bfa3d8127f0e3ca32aca1488a1b1c Mon Sep 17 00:00:00 2001 From: Jaco du Preez Date: Wed, 4 Nov 2026 12:18:59 +0200 Subject: [PATCH 02/14] docs(opencode): describe V2 plugin SDK and MCP config shape - .opencode/plugins/README.md: hook examples use the V2 tool name `shell`; remove the V1-only hook and logging comparisons. - docs/CONTRIBUTING.md: plugins default-export a `Plugin.define` from `@opencode/plugin`, matching the V2 build guide. - docs/framework/quick-start.md, setup-requirements.md: the Unity MCP block uses `mcp.servers` with the remote type and `disabled`. Refs: #107 --- .opencode/plugins/README.md | 9 +++------ docs/CONTRIBUTING.md | 2 +- docs/framework/quick-start.md | 4 ++-- docs/framework/setup-requirements.md | 8 ++++---- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.opencode/plugins/README.md b/.opencode/plugins/README.md index cf9ffdd..fdd149c 100644 --- a/.opencode/plugins/README.md +++ b/.opencode/plugins/README.md @@ -27,7 +27,7 @@ export default Plugin.define({ const controller = new AbortController() await ctx.tool.hook("execute.before", (event) => { - if (event.tool === "bash") { + if (event.tool === "shell") { const input = event.input as { command?: string } console.info("[example] command", input.command) } @@ -88,14 +88,11 @@ await ctx.session.hook("compaction", (event) => { }) ``` -OpenCode V2 has no direct equivalent of the V1 -`experimental.compaction.autocontinue` hook. Use the public -`session.compacted` event for post-compaction notifications. +For post-compaction notifications, use the public `session.compacted` event. ## Logging and Errors -The V2 context does not expose the V1 `client.app.log` method. Use the -supported console methods for plugin diagnostics: +Use the console methods for plugin diagnostics: ```typescript console.info("[example] plugin loaded", { directory: ctx.location.directory }) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index f1310de..4a5ca42 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -243,7 +243,7 @@ See `.opencode/plugins/README.md` for the complete plugin architecture guide. Briefly: 1. Create `{plugin-name}.ts` in `.opencode/plugins/` -2. Export a `Plugin` instance conforming to `@opencode-ai/plugin` +2. Default-export a V2 plugin definition (`Plugin.define` from `@opencode/plugin`) with a stable `id` and `setup(ctx)` 3. Register in `opencode.json` under the `plugins` array 4. Write tests in `.opencode/plugins/tests/test-{name}.mjs` 5. Document the plugin in `.opencode/plugins/README.md` diff --git a/docs/framework/quick-start.md b/docs/framework/quick-start.md index 8762e74..598f749 100644 --- a/docs/framework/quick-start.md +++ b/docs/framework/quick-start.md @@ -238,8 +238,8 @@ If you already know what you need, jump directly to the relevant path: # In Unity: Window → Package Manager → + → Add package from git URL # Paste: https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#main ``` - Then add the `mcp.unity` block to your MCP config (`opencode.json` for OpenCode, `pi.json` for Pi) and set - `enabled: true` once Unity Editor is running. See `/setup-engine` §7.4 + Then add the `mcp.servers.unity` block to your MCP config (`opencode.json` for OpenCode, `pi.json` for Pi) and set + `disabled` to `false` once Unity Editor is running. See `/setup-engine` §7.4 for full details. 5. **Validate the concept** — Run `/design-review design/gdd/game-concept.md` 6. **Decompose into systems** — Run `/map-systems` to map all systems and dependencies diff --git a/docs/framework/setup-requirements.md b/docs/framework/setup-requirements.md index 556d69f..006b7b9 100644 --- a/docs/framework/setup-requirements.md +++ b/docs/framework/setup-requirements.md @@ -120,17 +120,17 @@ Then click **Window → MCP for Unity → "Configure All Detected Clients"** to **Configuration:** -Add this block to the `mcp` object in your `opencode.json` (the wizard may auto-configure, but manual is the reliable fallback): +Add this block to `mcp.servers` in your `opencode.json` (the wizard may auto-configure, but manual is the reliable fallback): ```json "unity": { - "type": "local", + "type": "remote", "url": "http://localhost:8080/mcp", - "enabled": false + "disabled": true } ``` -Flip `enabled` to `true` once Unity Editor is running. +Set `disabled` to `false` once Unity Editor is running. > ⚠ **Unity Editor must be running** before OCGS agents can use unity-mcp tools. If the Editor is closed, MCP calls will fail with a connection error. Open your project in Unity, then continue. From fd977721bac9282fa6c7ce61ff6e9407ce88abfd Mon Sep 17 00:00:00 2001 From: Jaco du Preez Date: Wed, 4 Nov 2026 12:20:07 +0200 Subject: [PATCH 03/14] feat(qa): add autopilot-playtest skill and command Adds an optional automated playtest path. The autopilot-playtest skill runs Autopilot against the open Godot project and writes a report to production/qa/playtests/. It stays disabled until the user opts in. - opencode.json: add `mcp.servers.autopilot`, disabled by default. - qa module: skill, command, modulefile, and installed manifest. - Mirror the skill and command to .agents/skills and .agents/commands. - skill-testing-framework/catalog.yaml: register the skill (invariant I10). - AGENTS.md and reference docs: skill and command counts 77 -> 78; add /autopilot-playtest to the QA row. The skill checks for autopilot_start_run before starting a run. The MCP tools appear a few seconds after the server starts, so the first model turn can run before they exist. Refs: #107 --- .agents/commands/autopilot-playtest.md | 8 ++ .agents/modules/installed.json | 2 + .../modules/qa/commands/autopilot-playtest.md | 8 ++ .agents/modules/qa/modulefile.yaml | 4 +- .../qa/skills/autopilot-playtest/SKILL.md | 90 +++++++++++++++++++ .agents/skills/autopilot-playtest/SKILL.md | 90 +++++++++++++++++++ AGENTS.md | 10 +-- docs/framework/directory-structure.md | 4 +- docs/framework/skills-reference.md | 2 +- opencode.json | 6 ++ skill-testing-framework/catalog.yaml | 11 +++ 11 files changed, 225 insertions(+), 10 deletions(-) create mode 100644 .agents/commands/autopilot-playtest.md create mode 100644 .agents/modules/qa/commands/autopilot-playtest.md create mode 100644 .agents/modules/qa/skills/autopilot-playtest/SKILL.md create mode 100644 .agents/skills/autopilot-playtest/SKILL.md diff --git a/.agents/commands/autopilot-playtest.md b/.agents/commands/autopilot-playtest.md new file mode 100644 index 0000000..c725e93 --- /dev/null +++ b/.agents/commands/autopilot-playtest.md @@ -0,0 +1,8 @@ +--- +name: autopilot-playtest +description: "Run an automated playtest of the open Godot game with Autopilot" +skill: autopilot-playtest +category: qa +--- + +Invokes `/autopilot-playtest` skill. diff --git a/.agents/modules/installed.json b/.agents/modules/installed.json index 231f21d..fb410ce 100644 --- a/.agents/modules/installed.json +++ b/.agents/modules/installed.json @@ -227,6 +227,7 @@ "agents/qa-lead.md", "agents/qa-tester.md", "agents/security-engineer.md", + "commands/autopilot-playtest.md", "commands/bug-report.md", "commands/bug-triage.md", "commands/qa-plan.md", @@ -242,6 +243,7 @@ "commands/test-setup.md", "rules/test-standards.md", "skills/automated-smoke-test/SKILL.md", + "skills/autopilot-playtest/SKILL.md", "skills/bug-report/SKILL.md", "skills/bug-triage/SKILL.md", "skills/perf-profile/SKILL.md", diff --git a/.agents/modules/qa/commands/autopilot-playtest.md b/.agents/modules/qa/commands/autopilot-playtest.md new file mode 100644 index 0000000..c725e93 --- /dev/null +++ b/.agents/modules/qa/commands/autopilot-playtest.md @@ -0,0 +1,8 @@ +--- +name: autopilot-playtest +description: "Run an automated playtest of the open Godot game with Autopilot" +skill: autopilot-playtest +category: qa +--- + +Invokes `/autopilot-playtest` skill. diff --git a/.agents/modules/qa/modulefile.yaml b/.agents/modules/qa/modulefile.yaml index b7e8d30..67b3ca8 100644 --- a/.agents/modules/qa/modulefile.yaml +++ b/.agents/modules/qa/modulefile.yaml @@ -7,10 +7,10 @@ provides: skills: [team-qa, qa-plan, smoke-check, soak-test, regression-suite, test-setup, test-helpers, test-flakiness, test-evidence-review, automated-smoke-test, bug-report, bug-triage, security-audit, perf-profile, team-polish, - skill-test, skill-improve] + skill-test, skill-improve, autopilot-playtest] commands: [team-qa, qa-plan, smoke-check, soak-test, regression-suite, test-setup, test-helpers, test-flakiness, test-evidence-review, bug-report, bug-triage, - security-audit, team-polish] + security-audit, team-polish, autopilot-playtest] rules: [test-standards] plugged-into: engines: [] diff --git a/.agents/modules/qa/skills/autopilot-playtest/SKILL.md b/.agents/modules/qa/skills/autopilot-playtest/SKILL.md new file mode 100644 index 0000000..1bcc2fb --- /dev/null +++ b/.agents/modules/qa/skills/autopilot-playtest/SKILL.md @@ -0,0 +1,90 @@ +--- +name: autopilot-playtest +description: "Playtest the game open in the Godot editor with Autopilot's MCP server: explore for bugs, test a level, verify a claim, or reach a goal in play. Files the result to production/qa/playtests/. Use when asked for an automated playtest or to check gameplay with Autopilot." +argument-hint: "[explore|test|verify|goal] [request]" +user-invocable: true +allowed-tools: Read, Glob, Grep, Write, question +--- + +# Automated Playtest with Autopilot + +Autopilot plays the game open in the Godot editor. You write the harness for this request, run it through Autopilot's MCP tools, and file what play showed. + +Autopilot's own instructions (`INSTRUCTIONS.md`, named by `start_run`) are the detailed reference. This skill is the framework's wrapper around them. + +**The result is automated evidence. It is never a human playtest.** + +--- + +## Setup (once per machine and project) + +Rationale and verified behaviour: `docs/framework/autopilot-integration.md`. + +- **Godot 4.7 or later.** The addon is tested on 4.7.2. This framework's Godot reference is pinned to 4.6 until it is refreshed for 4.7. +- **Addon.** Install `addons/autopilot/` into the game project from Autopilot's download. Enable it in Project Settings > Plugins, then commit the `project.godot` changes it makes (autoload `AutopilotPlaytest`, and `autopilot/project_id`). Do not copy the addon into this repository. +- **Agent service and Bun.** Install Autopilot's agent service, which provides the `autopilot` command that `opencode.json` runs. Install Bun, which Autopilot's default harness agent needs. +- **Keys.** Bring your own keys: a TypeSafe decision key and an LLM key, both in Autopilot's `autopilot.env`. Do not sign in to Aura. +- **Sharing.** In the dock's Privacy step, turn sharing off. Autopilot shares recordings, screenshots, traces and memories by default. +- **Releases.** Exclude `addons/autopilot/*` from export presets. + +--- + +## Phase 1: Preflight + +1. Confirm `opencode.json` → `mcp.servers.autopilot` has `disabled` set to `false` for this session. It ships with `disabled: true`. If it is `true`, ask the user whether to enable it for this session, then stop until they answer. Do not edit `opencode.json` yourself. +2. Readiness. The Autopilot tools join your tool list only after the MCP server starts. That took about 5 seconds in testing and can take longer on a cold start. Check that `autopilot_start_run` is among your tools. If it is not, make one cheap read-only call (for example `glob` on `production/session-state/*.md`) so the next turn sees the tools, then check again. Make at most 20 such checks. If the tool is still missing, stop and tell the user: "Autopilot's MCP server did not start. Check that the `autopilot` command is on PATH, then run `opencode mcp list`. Send the request again once `autopilot` shows connected." +3. Confirm the game's project is open in the Godot editor with the Autopilot dock connected. If `start_run` says the agent service isn't running, relay its message and stop. +4. Keys: never ask the user to paste a key into this chat. If a run asks for a key, tell the user where it goes (see Setup). +5. Sharing: confirm the dock's Privacy step is off (see Setup). If the user has not confirmed this, ask before the first run. + +## Phase 2: Choose the kind and start the run + +Map the request to a kind: + +- `verify`: a claim to check, e.g. "the door opens with the key". Result: CONFIRMED, DENIED or INCONCLUSIVE. +- `goal`: an outcome to reach in play, e.g. "sell three crops". Result: achieved or not achieved. +- `explore`: look for bugs, or test a level or system, e.g. "go through the level and tell me what looks wrong". A test is an exploration. + +Call `start_run` with `request` (the user's words, verbatim) and `kind`. Add `minutes` (explorations; default 10), `time_limit_minutes` (default 30) and `level` only when the user gives them. + +Start one run per request unless the user asks for more. Each run costs model tokens. + +## Phase 3: Build the harness + +1. Read the `INSTRUCTIONS.md` that `start_run` names, in full, with the MCP `read_file` tool. It is long: read it in parts. Follow it. +2. Write the harness and notes with `write_file` or `edit_file`. Those tools reach only this run's folder, `notes.md` and `lib.py` in Autopilot's memory folder for the project. They cannot touch this repository. +3. Read the game's scripts from this repository with Read, Glob and Grep as needed. Autopilot's own copy of the code is in its memory folder. +4. Never try to change game files. Autopilot acts on the game through play only. + +## Phase 4: Run and wait + +- A call that answers "still going" has not finished. Call `wait` until it returns the result. Do not call other game tools in the meantime. +- The user can watch, stop or steer the run in the Autopilot dock. Steering text arrives with a later tool result: follow it. +- Pictures: `describe_frames` needs a vision model. Autopilot's guide assumes a frame-describer subagent, which only Claude Code and Codex get from `autopilot connect`. Under OpenCode, call `describe_frames` only if this session's model reads images. Otherwise say the run could not check visuals, and base the report on state data. + +## Phase 5: Report + +When `report` says "Reported", the run is over. Ask: + +> May I write this playtest report to `production/qa/playtests/autopilot-[date]-[slug].md`? + +Then write the report with: + +- Kind, request (verbatim), level, time limit used, and run id. +- Outcome: the verdict or achieved state, with Autopilot's reason and evidence. +- Findings, each marked **Unverified (Autopilot)**. Confirm a bug in the game before filing it with `/bug-report`. +- Recording location (the dock's run folder, `user://playtest/