From 62d6a13ac9228e5ea9ae0e56811b7fd4479563cb Mon Sep 17 00:00:00 2001 From: Natalie Date: Sun, 17 May 2026 02:19:58 -0700 Subject: [PATCH] =?UTF-8?q?feat(@projects/@magic-civilization):=20?= =?UTF-8?q?=E2=9C=A8=20add=20auto-respawn=20harness=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- src/simulator/api-gdext/src/player_api.rs | 1 - tooling/claude-player-mcp/src/harness.ts | 6 ++++++ tooling/claude-player-mcp/src/index.ts | 15 +++++++++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/simulator/api-gdext/src/player_api.rs b/src/simulator/api-gdext/src/player_api.rs index dcef1347..74609592 100644 --- a/src/simulator/api-gdext/src/player_api.rs +++ b/src/simulator/api-gdext/src/player_api.rs @@ -276,7 +276,6 @@ fn normalize_int_zero_floats(src: &str) -> String { j += 1; } if bytes[j].is_ascii_digit() { - let digit_start = j; while j < bytes.len() && bytes[j].is_ascii_digit() { j += 1; } diff --git a/tooling/claude-player-mcp/src/harness.ts b/tooling/claude-player-mcp/src/harness.ts index 9c7428c6..dcbd3caa 100644 --- a/tooling/claude-player-mcp/src/harness.ts +++ b/tooling/claude-player-mcp/src/harness.ts @@ -47,6 +47,12 @@ export class HarnessClient { private nextId = 1; private closed = false; + /** True after the child exits (clean or crash). Lets the MCP layer + * detect a dead harness and respawn instead of erroring forever. */ + get isClosed(): boolean { + return this.closed; + } + constructor(opts: HarnessOptions) { this.opts = { env: {}, diff --git a/tooling/claude-player-mcp/src/index.ts b/tooling/claude-player-mcp/src/index.ts index 426d55f8..6dc4533a 100644 --- a/tooling/claude-player-mcp/src/index.ts +++ b/tooling/claude-player-mcp/src/index.ts @@ -108,8 +108,19 @@ interface HarnessHolder { const holder: HarnessHolder = { client: null }; function getOrCreateClient(): HarnessClient { - if (holder.client !== null) return holder.client; - emitStderr(`spawning harness: ${serverScript()}`); + // Auto-respawn: if the held client died (godot child crashed, was killed by + // a developer iterating on the dylib, etc.) treat it as `null` so the next + // request gets a fresh harness. Without this, every Claude Code session + // that hit a single harness crash would need a full restart to recover. + if (holder.client !== null && !holder.client.isClosed) { + return holder.client; + } + if (holder.client !== null) { + emitStderr("previous harness closed; respawning"); + holder.client = null; + } else { + emitStderr(`spawning harness: ${serverScript()}`); + } const client = new HarnessClient({ serverScript: serverScript(), env: harnessEnv(),