From 9510955e2b5057dcfa63467704f09bd7439bd732 Mon Sep 17 00:00:00 2001 From: "Molecule AI Dev Engineer A (Kimi)" Date: Fri, 19 Jun 2026 00:02:31 +0000 Subject: [PATCH] auto-heal: re-declare molecule-platform-mcp before restarting vanilla concierge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #3047. The management MCP declaration in applyConciergeProvisionConfig runs on create/full provision, but an existing vanilla concierge that reaches MaybeProvisionPlatformAgentOnBoot's running-but-vanilla path was only restarted without ensuring the declared plugin row existed first. Re-run seedTemplatePlugins before the restart so the post-restart reconcile + boot-install see the declaration and wire molecule-platform-mcp. - Update MaybeProvisionPlatformOnBoot to call seedTemplatePlugins for the concierge before scheduling the restart. - Update TestMaybeProvisionPlatformAgentOnBoot_RestartsRunningButVanilla to expect the kind precheck + workspace_declared_plugins INSERT. Fixes #3047 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- .../internal/handlers/platform_agent.go | 16 ++++++++++------ .../internal/handlers/platform_agent_test.go | 9 +++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/workspace-server/internal/handlers/platform_agent.go b/workspace-server/internal/handlers/platform_agent.go index 74126655..7c8d0991 100644 --- a/workspace-server/internal/handlers/platform_agent.go +++ b/workspace-server/internal/handlers/platform_agent.go @@ -613,18 +613,22 @@ func MaybeProvisionPlatformAgentOnBoot(ctx context.Context, database *sql.DB, pr // Already online AND a live container? Then it's running — but it may be a // concierge that pre-dates the identity overlay (booted as a vanilla // claude-code agent with no system-prompt.md). Probe for the concierge - // identity; if it's missing, restart ONCE so the provision path re-seeds the - // overlay. This is what makes the seed idempotent + self-applying on the - // EXISTING concierge (the deterministic self-hosted id), not just new - // installs. IsRunning is the authoritative liveness check; status is the - // cheap one. + // identity; if it's missing, re-declare the management MCP plugin in the DB + // BEFORE restarting so the post-restart reconcile + boot-install see it, then + // restart ONCE so the provision path re-seeds the overlay. This is what makes + // the seed idempotent + self-applying on the EXISTING concierge (the + // deterministic self-hosted id), not just new installs. IsRunning is the + // authoritative liveness check; status is the cheap one. running, _ := prov.IsRunning(ctx, id) if running { if conciergeIdentityPresent(ctx, prov, id) { log.Printf("boot: platform-agent %s already running with concierge identity — skipping", id) return } - log.Printf("boot: platform-agent %s running but MISSING concierge identity — restarting once to apply the system prompt + platform MCP", id) + log.Printf("boot: platform-agent %s running but MISSING concierge identity — re-declaring management MCP and restarting once to apply the system prompt + platform MCP", id) + if rec, skip := seedTemplatePlugins(ctx, id, []string{conciergePlatformMCPPlugin}); skip > 0 { + log.Printf("boot: concierge %s could not re-declare %q plugin (recorded=%d skipped=%d) — management MCP may be absent until next provision", id, conciergePlatformMCPPlugin, rec, skip) + } go restartByID(id) return } diff --git a/workspace-server/internal/handlers/platform_agent_test.go b/workspace-server/internal/handlers/platform_agent_test.go index 53e0b0dc..122acf87 100644 --- a/workspace-server/internal/handlers/platform_agent_test.go +++ b/workspace-server/internal/handlers/platform_agent_test.go @@ -312,6 +312,15 @@ func TestMaybeProvisionPlatformAgentOnBoot_RestartsRunningButVanilla(t *testing. mock.ExpectQuery(`SELECT id, status FROM workspaces WHERE kind = 'platform'`). WillReturnRows(sqlmock.NewRows([]string{"id", "status"}).AddRow(bootPlatformID, "online")) + // The running-but-vanilla path re-declares the management MCP plugin before + // restarting so the post-restart boot-install sees the declaration. + const kindQuery = `SELECT COALESCE\(kind, 'workspace'\) FROM workspaces WHERE id =` + mock.ExpectQuery(kindQuery).WithArgs(bootPlatformID). + WillReturnRows(sqlmock.NewRows([]string{"kind"}).AddRow("platform")) + mock.ExpectExec(`INSERT INTO workspace_declared_plugins`). + WithArgs(bootPlatformID, conciergePlatformMCPPlugin, sqlmock.AnyArg()). + WillReturnResult(sqlmock.NewResult(0, 1)) + // Running, but ExecRead of system-prompt.md returns vanilla content (no // "Org Concierge") → identity absent → restart. prov := &stubBootProvExec{stubBootProv: stubBootProv{running: true}, systemPrompt: "generic coding assistant"} -- 2.52.0