From 20252250c3a3bbd624ff6cfe2b21ae0fa52f04c8 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Sun, 21 Jun 2026 02:23:14 -0700 Subject: [PATCH] test(ssot): derive concierge degraded-gate tool id from the delivery contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The online/degraded gate hard-coded conciergePlatformMCPCreateWorkspaceTool = "mcp__molecule-platform__create_workspace" independently of the delivery contract's mcp_server_name. That let the platform-agent template drift its MCP server name to "platform" (so the runtime emitted mcp__platform__create_workspace) while the gate kept looking for "molecule-platform" — marking every concierge degraded even with all platform tools loaded. Enforce the SSOT (contract mcp_server_name = "molecule-platform"): - Add MCPServerName to MCPPluginDeliveryContract (already in the contract JSON). - Pin mcp_server_name in TestMCPPluginDeliveryContract_MatchesSSOT. - TestSSOT_DegradeGateToolDerivesFromContract asserts the gate's tool id == "mcp__" + contract.mcp_server_name + "__create_workspace", so the gate and the contract can never silently diverge again. Paired with molecule-ai-workspace-template-platform-agent#6, which aligns the runtime mcp_servers.yaml server name to molecule-platform. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../handlers/mcp_plugin_delivery_contract.go | 7 +++++ .../mcp_plugin_delivery_contract_test.go | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/workspace-server/internal/handlers/mcp_plugin_delivery_contract.go b/workspace-server/internal/handlers/mcp_plugin_delivery_contract.go index dd050d7e0..842e88302 100644 --- a/workspace-server/internal/handlers/mcp_plugin_delivery_contract.go +++ b/workspace-server/internal/handlers/mcp_plugin_delivery_contract.go @@ -24,6 +24,13 @@ type MCPPluginDeliveryContract struct { EntryShape string `json:"entry_shape"` Producer string `json:"producer"` Consumer string `json:"consumer"` + // MCPServerName is the SSOT for the platform MCP server NAME — the mcpServers + // key the runtime registers the server under, which Claude Code turns into + // the tool-id prefix `mcp____`. The platform-agent + // template's mcp_servers.yaml and the online/degraded gate's expected tool id + // (conciergePlatformMCPCreateWorkspaceTool) both derive from this value — see + // TestSSOT_DegradeGateToolDerivesFromContract. + MCPServerName string `json:"mcp_server_name"` } // LoadMCPPluginDeliveryContract loads the contract from the repo root. diff --git a/workspace-server/internal/handlers/mcp_plugin_delivery_contract_test.go b/workspace-server/internal/handlers/mcp_plugin_delivery_contract_test.go index cf54ca5ae..33ca38ee8 100644 --- a/workspace-server/internal/handlers/mcp_plugin_delivery_contract_test.go +++ b/workspace-server/internal/handlers/mcp_plugin_delivery_contract_test.go @@ -35,6 +35,32 @@ func TestMCPPluginDeliveryContract_MatchesSSOT(t *testing.T) { if c.Consumer != "claude_sdk_executor._load_settings_mcp" { t.Errorf("consumer = %q, want claude_sdk_executor._load_settings_mcp", c.Consumer) } + if c.MCPServerName != "molecule-platform" { + t.Errorf("mcp_server_name = %q, want molecule-platform", c.MCPServerName) + } +} + +// TestSSOT_DegradeGateToolDerivesFromContract enforces that the online/degraded +// gate's expected platform tool id is DERIVED from the contract's +// mcp_server_name (the SSOT), not an independent hardcode. If the contract's +// server name changes (or the constant drifts), this fails — preventing the +// class of bug where the gate looked for mcp__molecule-platform__create_workspace +// while the runtime (mcp_servers.yaml name: platform) emitted +// mcp__platform__create_workspace, marking every concierge degraded. +func TestSSOT_DegradeGateToolDerivesFromContract(t *testing.T) { + c, err := LoadMCPPluginDeliveryContract() + if err != nil { + t.Fatalf("load contract: %v", err) + } + if c.MCPServerName == "" { + t.Fatal("contract mcp_server_name is empty — SSOT for the platform MCP tool prefix") + } + want := "mcp__" + c.MCPServerName + "__create_workspace" + if conciergePlatformMCPCreateWorkspaceTool != want { + t.Errorf("SSOT drift: conciergePlatformMCPCreateWorkspaceTool = %q, but contract mcp_server_name = %q implies %q.\n"+ + "The degraded gate must look for the tool id the runtime actually emits (mcp____create_workspace).", + conciergePlatformMCPCreateWorkspaceTool, c.MCPServerName, want) + } } // TestMCPPluginDeliveryContract_LoadableFromRepoRoot guards against a moved -- 2.52.0