test(routing): pin tool-schema parity with supervisor

Captures the four routed skills' (review, debug, retrospective, trainer)
tool definitions as a JSON snapshot and asserts the routing pod's registry
advertises byte-equal schemas. A deliberate schema change fails this test,
requiring an intentional snapshot update in lockstep with consumers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mathias Bergqvist
2026-05-04 22:59:06 +02:00
parent 3a99d5e20e
commit 751f410ca6
2 changed files with 177 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
package routing_test
import (
"context"
"encoding/json"
"os"
"sort"
"testing"
"github.com/mathiasbq/supervisor/internal/registry"
"github.com/mathiasbq/supervisor/internal/skills/debug"
"github.com/mathiasbq/supervisor/internal/skills/retrospective"
"github.com/mathiasbq/supervisor/internal/skills/review"
"github.com/mathiasbq/supervisor/internal/skills/trainer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestToolsListMatchesSupervisorSnapshot pins the four routed skills' tool
// definitions to the supervisor's current advertisement. A deliberate schema
// change must be reflected here by updating testdata/tools_list.snapshot.json.
func TestToolsListMatchesSupervisorSnapshot(t *testing.T) {
complete := func(_ context.Context, _, _, _ string) (string, int64, error) {
return "", 0, nil
}
reg := registry.New()
reg.Register(review.New(review.Config{
SkillPrompt: "stub",
DefaultModel: "stub",
CompleteFunc: complete,
}))
reg.Register(debug.New(debug.Config{
SkillPrompt: "stub",
DefaultModel: "stub",
CompleteFunc: complete,
}))
reg.Register(retrospective.New(retrospective.Config{
SkillPrompt: "stub",
DefaultModel: "stub",
CompleteFunc: complete,
}))
reg.Register(trainer.New(trainer.Config{
ReaderPrompt: "stub",
WriterPrompt: "stub",
DefaultModel: "stub",
CompleteFunc: complete,
}))
wanted := map[string]bool{
"review": true,
"debug": true,
"retrospective": true,
"trainer": true,
}
var routed []registry.ToolDef
for _, td := range reg.Tools() {
if wanted[td.Name] {
routed = append(routed, td)
}
}
sort.Slice(routed, func(i, j int) bool { return routed[i].Name < routed[j].Name })
got, err := json.MarshalIndent(routed, "", " ")
require.NoError(t, err)
want, err := os.ReadFile("testdata/tools_list.snapshot.json")
require.NoError(t, err)
// Normalize both via re-encode so whitespace differences don't dominate.
var gotV, wantV any
require.NoError(t, json.Unmarshal(got, &gotV))
require.NoError(t, json.Unmarshal(want, &wantV))
gotN, _ := json.MarshalIndent(gotV, "", " ")
wantN, _ := json.MarshalIndent(wantV, "", " ")
assert.Equal(t, string(wantN), string(gotN),
"tool advertisement drifted from supervisor snapshot — update testdata/tools_list.snapshot.json deliberately if the schema change is intentional")
}

View File

@@ -0,0 +1,97 @@
[
{
"name": "debug",
"description": "Consult a local model to analyse an error and return hypotheses ordered by likelihood, each with a concrete verification step.",
"inputSchema": {
"properties": {
"context": {
"type": "string"
},
"error": {
"type": "string"
},
"model": {
"type": "string"
},
"project_root": {
"type": "string"
},
"session_id": {
"type": "string"
}
},
"required": [
"project_root",
"error"
],
"type": "object"
}
},
{
"name": "retrospective",
"description": "Consult a local model to analyse a completed session and identify what is novel or worth preserving as organizational knowledge.",
"inputSchema": {
"type": "object",
"required": [
"session_id"
],
"properties": {
"session_id": {
"type": "string"
},
"model": {
"type": "string"
}
}
}
},
{
"name": "review",
"description": "Consult a local model for a structured code review of the specified files. Returns findings with severity levels.",
"inputSchema": {
"properties": {
"context": {
"type": "string"
},
"files": {
"items": {
"type": "string"
},
"type": "array"
},
"model": {
"type": "string"
},
"project_root": {
"type": "string"
},
"session_id": {
"type": "string"
}
},
"required": [
"project_root",
"files"
],
"type": "object"
}
},
{
"name": "trainer",
"description": "Consult a local model to identify learning moments from a session log and suggest knowledge to preserve in the brain.",
"inputSchema": {
"properties": {
"model": {
"type": "string"
},
"session_id": {
"type": "string"
}
},
"required": [
"session_id"
],
"type": "object"
}
}
]