From 88782de07ca00c5459d8cfa498168b597810621a Mon Sep 17 00:00:00 2001 From: Mathias Bergqvist Date: Tue, 5 May 2026 07:13:24 +0200 Subject: [PATCH] feat(hyperguild): mode client-local writes routing headers Plan 6 is now deployed; replace the _routing_pending placeholder in the routing MCP entry with a real headers block carrying X-Hyperguild-Mode: client-local. The pod treats absent or unknown values as client-local, so this is forward-compat for future modes. Co-Authored-By: Claude Sonnet 4.6 --- cmd/hyperguild/README.md | 7 ++++--- cmd/hyperguild/mode.go | 8 +++++--- cmd/hyperguild/mode_test.go | 29 +++++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/cmd/hyperguild/README.md b/cmd/hyperguild/README.md index 7fdf59b..bc73922 100644 --- a/cmd/hyperguild/README.md +++ b/cmd/hyperguild/README.md @@ -115,9 +115,10 @@ Flags: Modes: - **cloud** — brain MCP only. Claude Code with no routing. -- **client-local** — brain + routing placeholder. The routing entry's - URL points at `koala:30310/mcp`; a `_routing_pending` field marks it - as awaiting Plan 6 of the hyperguild migration. +- **client-local** — brain + routing pod. The `routing` entry points at + `koala:30310/mcp` (the routing pod, deployed in Plan 6). The + `X-Hyperguild-Mode: client-local` header is forward-compat for future + modes; the pod treats absent or unknown values as `client-local`. - **sovereign** — brain only, with a `_mode_note` explaining that this mode primarily uses Crush + LiteLLM and the `.mcp.json` is a Claude Code fallback for emergency offline use. diff --git a/cmd/hyperguild/mode.go b/cmd/hyperguild/mode.go index db4a583..51cd3a9 100644 --- a/cmd/hyperguild/mode.go +++ b/cmd/hyperguild/mode.go @@ -78,9 +78,11 @@ func modeClientLocal(brainURL string) map[string]any { "description": "Brain MCP — knowledge query, write, ingestion, session log", }, "routing": map[string]any{ - "url": "http://koala:30310/mcp", - "description": "Mode 2 routing pod — routes skill calls to LiteLLM/local", - "_routing_pending": "Plan 6 — routing pod not deployed yet; this URL is a placeholder", + "url": "http://koala:30310/mcp", + "description": "Mode 2 routing pod — routes skill calls to LiteLLM/local", + "headers": map[string]any{ + "X-Hyperguild-Mode": "client-local", + }, }, }, } diff --git a/cmd/hyperguild/mode_test.go b/cmd/hyperguild/mode_test.go index 43bbc5f..077b2c3 100644 --- a/cmd/hyperguild/mode_test.go +++ b/cmd/hyperguild/mode_test.go @@ -39,7 +39,7 @@ func TestRunMode_Cloud_Default(t *testing.T) { assert.NotContains(t, got, "_mode_note") } -func TestRunMode_ClientLocal_HasRoutingPlaceholder(t *testing.T) { +func TestRunMode_ClientLocal_HasRoutingEntry(t *testing.T) { dir := t.TempDir() outPath := filepath.Join(dir, ".mcp.json") t.Setenv("BRAIN_URL", "http://koala:30330") @@ -54,7 +54,32 @@ func TestRunMode_ClientLocal_HasRoutingPlaceholder(t *testing.T) { require.Contains(t, servers, "routing") routing := servers["routing"].(map[string]any) - assert.Contains(t, routing, "_routing_pending") + assert.NotContains(t, routing, "_routing_pending", "placeholder should be removed once Plan 6 ships") + + headers, ok := routing["headers"].(map[string]any) + require.True(t, ok, "routing entry should have headers block") + assert.Equal(t, "client-local", headers["X-Hyperguild-Mode"]) +} + +func TestModeClientLocalHasRoutingHeader(t *testing.T) { + tmp := t.TempDir() + "/mcp.json" + out := &bytes.Buffer{} + stderr := &bytes.Buffer{} + require.NoError(t, runMode(context.Background(), []string{"client-local", "--out", tmp}, nil, out, stderr)) + + body, err := os.ReadFile(tmp) + require.NoError(t, err) + var doc map[string]any + require.NoError(t, json.Unmarshal(body, &doc)) + + servers := doc["mcpServers"].(map[string]any) + routing := servers["routing"].(map[string]any) + assert.Equal(t, "http://koala:30310/mcp", routing["url"]) + assert.NotContains(t, routing, "_routing_pending", "placeholder should be removed once Plan 6 ships") + + headers, ok := routing["headers"].(map[string]any) + require.True(t, ok, "routing entry should have headers block") + assert.Equal(t, "client-local", headers["X-Hyperguild-Mode"]) } func TestRunMode_Sovereign_HasModeNote(t *testing.T) {