Pulse/internal/agentcapabilities/surface_contract_test.go
rcourtman ee8a24e14a backend and governance: MCP contract, agent capabilities, API, and release-control
Manifest-backed MCP tools, prompts, and resources with surface affordance contracts; agent capability manifest and governance projection; API contract tests and capability route projection; operations-loop and intelligence-funnel telemetry; release-control subsystem documentation, registry, and tooling; licensing and configuration.
2026-06-23 17:26:15 +01:00

518 lines
21 KiB
Go

package agentcapabilities
import (
"reflect"
"testing"
)
func TestDefaultSurfaceAffordancesPinSupportedSurfaces(t *testing.T) {
assistant := DefaultSurfaceAffordancesForID(SurfaceIDPulseAssistant)
if !assistant.Tools || !assistant.InteractiveQuestions {
t.Fatalf("Pulse Assistant affordances = %+v, want tools and interactive questions", assistant)
}
if assistant.Resources || assistant.Prompts || assistant.CapabilityMetadata {
t.Fatalf("Pulse Assistant must not default to MCP-only affordances: %+v", assistant)
}
mcp := DefaultSurfaceAffordancesForID(SurfaceIDPulseMCP)
if !mcp.Tools || !mcp.Resources || !mcp.Prompts || !mcp.CapabilityMetadata {
t.Fatalf("Pulse MCP affordances = %+v, want tools/resources/prompts/capability metadata", mcp)
}
if mcp.InteractiveQuestions {
t.Fatalf("Pulse MCP must not default to native interactive questions: %+v", mcp)
}
}
func TestNormalizeSurfaceAffordancesPreservesExplicitManifestValues(t *testing.T) {
surface := OperatorSurfaceContract{
ID: SurfaceIDPulseMCP,
Affordances: SurfaceAffordanceContract{
Tools: true,
},
}
got := NormalizeSurfaceAffordances(surface)
if !got.Tools {
t.Fatalf("explicit tools affordance lost: %+v", got)
}
if got.Resources || got.Prompts || got.CapabilityMetadata || got.InteractiveQuestions {
t.Fatalf("explicit affordances must not be expanded by defaults: %+v", got)
}
}
func TestResolveSurfaceAffordanceContractResolvesLabelsAndDefaults(t *testing.T) {
canonical, ok := ResolveSurfaceAffordanceContract(CanonicalManifest().SurfaceContract, SurfaceIDPulseMCP, "")
if !ok {
t.Fatal("canonical Pulse MCP surface must resolve from manifest")
}
if canonical.SurfaceID != SurfaceIDPulseMCP || canonical.SurfaceLabel != "Pulse MCP" {
t.Fatalf("canonical Pulse MCP surface identity = %+v", canonical)
}
if !canonical.Affordances.Tools || !canonical.Affordances.Resources || !canonical.Affordances.Prompts || !canonical.Affordances.CapabilityMetadata {
t.Fatalf("canonical Pulse MCP affordances = %+v", canonical.Affordances)
}
legacyAssistant, ok := ResolveSurfaceAffordanceContract(SurfaceContract{}, SurfaceIDPulseAssistant, "")
if ok {
t.Fatal("legacy Assistant fallback must not report a declared surface")
}
if legacyAssistant.SurfaceLabel != "Pulse Assistant" || !legacyAssistant.Affordances.Tools || !legacyAssistant.Affordances.InteractiveQuestions {
t.Fatalf("legacy Assistant fallback = %+v", legacyAssistant)
}
if legacyAssistant.Affordances.Resources || legacyAssistant.Affordances.Prompts || legacyAssistant.Affordances.CapabilityMetadata {
t.Fatalf("legacy Assistant fallback must not inherit MCP affordances: %+v", legacyAssistant.Affordances)
}
custom, ok := ResolveSurfaceAffordanceContract(SurfaceContract{}, "custom_external_agent", "")
if ok {
t.Fatal("unknown custom surface fallback must not report a declared surface")
}
if custom.SurfaceLabel != "custom_external_agent" || SurfaceAffordancesDeclared(custom.Affordances) {
t.Fatalf("unknown custom surface fallback = %+v, want id label and empty affordances", custom)
}
}
func TestManifestSurfaceAffordancesResolvesDeclaredAndDefaultSurfaces(t *testing.T) {
defaults, ok := ManifestSurfaceAffordances(Manifest{}, SurfaceIDPulseMCP)
if ok {
t.Fatal("legacy manifest fallback must report compatibility default, not a declared surface")
}
if !defaults.Tools || !defaults.Resources || !defaults.Prompts || !defaults.CapabilityMetadata || defaults.InteractiveQuestions {
t.Fatalf("Pulse MCP default affordances = %+v", defaults)
}
manifest := Manifest{SurfaceContract: SurfaceContract{OperatorSurfaces: []OperatorSurfaceContract{{
ID: "custom_external_agent",
Label: "Custom external agent",
ExternalAdapter: true,
Affordances: SurfaceAffordanceContract{
Tools: true,
Resources: true,
},
}}}}
declared, ok := ManifestSurfaceAffordances(manifest, "custom_external_agent")
if !ok {
t.Fatal("declared custom surface was not found")
}
if !declared.Tools || !declared.Resources || declared.Prompts || declared.CapabilityMetadata || declared.InteractiveQuestions {
t.Fatalf("declared custom surface affordances = %+v", declared)
}
if unknown, ok := ManifestSurfaceAffordances(Manifest{}, "unknown_surface"); ok || SurfaceAffordancesDeclared(unknown) {
t.Fatalf("unknown surface affordances = %+v ok=%v, want empty", unknown, ok)
}
}
func TestSurfaceAffordanceLabelsAreStable(t *testing.T) {
got := SurfaceAffordanceLabels(SurfaceAffordanceContract{
Tools: true,
Resources: true,
Prompts: true,
CapabilityMetadata: true,
InteractiveQuestions: true,
})
want := []string{"tools", "resources", "prompts", "capability metadata", "interactive questions"}
if len(got) != len(want) {
t.Fatalf("labels = %#v, want %#v", got, want)
}
for i := range want {
if got[i] != want[i] {
t.Fatalf("labels = %#v, want %#v", got, want)
}
}
}
func TestFindOperatorSurfaceContractMatchesIDOrLabel(t *testing.T) {
contract := CanonicalManifest().SurfaceContract
if surface, ok := FindOperatorSurfaceContract(contract, SurfaceIDPulseAssistant, ""); !ok || surface.Label != "Pulse Assistant" {
t.Fatalf("find by id = %+v ok=%v", surface, ok)
}
if surface, ok := FindOperatorSurfaceContract(contract, "", "Pulse MCP"); !ok || surface.ID != SurfaceIDPulseMCP {
t.Fatalf("find by label = %+v ok=%v", surface, ok)
}
}
func TestSurfaceToolContractNormalizeCollectionsDetachesNames(t *testing.T) {
source := []string{PulseQueryToolName}
contract := SurfaceToolContract{ToolNames: source}.NormalizeCollections()
if len(contract.ToolNames) != 1 || contract.ToolNames[0] != PulseQueryToolName {
t.Fatalf("normalized names = %#v", contract.ToolNames)
}
contract.ToolNames[0] = PulseReadToolName
if source[0] != PulseQueryToolName {
t.Fatalf("NormalizeCollections returned aliased tool names: source=%#v normalized=%#v", source, contract.ToolNames)
}
empty := SurfaceToolContract{}.NormalizeCollections()
if empty.ToolNames == nil || len(empty.ToolNames) != 0 {
t.Fatalf("empty tool names = %#v, want stable empty list", empty.ToolNames)
}
}
func TestCloneSurfaceToolContractsDetachesNames(t *testing.T) {
if empty := CloneSurfaceToolContracts(nil); empty == nil || len(empty) != 0 {
t.Fatalf("empty surface tool contracts = %#v, want stable empty list", empty)
}
source := []SurfaceToolContract{{
SurfaceID: SurfaceIDPulseMCP,
ToolNames: []string{FleetContextCapabilityName},
CapabilityNames: []string{FleetContextCapabilityName},
RegistryToolNames: []string{PulseReadToolName},
NativeToolNames: []string{PulseQuestionToolName},
}}
cloned := CloneSurfaceToolContracts(source)
source[0].ToolNames[0] = "mutated"
source[0].CapabilityNames[0] = "mutated"
source[0].RegistryToolNames[0] = "mutated"
source[0].NativeToolNames[0] = "mutated"
if !reflect.DeepEqual(cloned[0].ToolNames, []string{FleetContextCapabilityName}) {
t.Fatalf("cloned tool names = %#v", cloned[0].ToolNames)
}
if !reflect.DeepEqual(cloned[0].CapabilityNames, []string{FleetContextCapabilityName}) {
t.Fatalf("cloned capability names = %#v", cloned[0].CapabilityNames)
}
if !reflect.DeepEqual(cloned[0].RegistryToolNames, []string{PulseReadToolName}) {
t.Fatalf("cloned registry names = %#v", cloned[0].RegistryToolNames)
}
if !reflect.DeepEqual(cloned[0].NativeToolNames, []string{PulseQuestionToolName}) {
t.Fatalf("cloned native names = %#v", cloned[0].NativeToolNames)
}
}
func TestProjectPulseAssistantSurfaceToolContractSeparatesRegistryAndNativeTools(t *testing.T) {
contract, ok := ProjectPulseAssistantSurfaceToolContract(CanonicalManifest().SurfaceContract, []ProviderTool{
{Name: PulseQueryToolName},
{Name: PulseControlToolName},
NewPulseQuestionProviderTool(),
})
if !ok {
t.Fatal("canonical manifest must declare Pulse Assistant surface")
}
if contract.SurfaceID != SurfaceIDPulseAssistant || contract.SurfaceLabel != "Pulse Assistant" {
t.Fatalf("Assistant surface identity = %+v", contract)
}
if contract.ToolSource != SurfaceToolSourceAssistantRegistry {
t.Fatalf("Assistant tool source = %q, want %s", contract.ToolSource, SurfaceToolSourceAssistantRegistry)
}
if !reflect.DeepEqual(contract.ToolNames, []string{PulseQueryToolName, PulseControlToolName, PulseQuestionToolName}) {
t.Fatalf("Assistant tool names = %#v", contract.ToolNames)
}
if !reflect.DeepEqual(contract.RegistryToolNames, []string{PulseQueryToolName, PulseControlToolName}) {
t.Fatalf("Assistant registry names = %#v", contract.RegistryToolNames)
}
if !reflect.DeepEqual(contract.NativeToolNames, []string{PulseQuestionToolName}) {
t.Fatalf("Assistant native names = %#v", contract.NativeToolNames)
}
if len(contract.CapabilityNames) != 0 {
t.Fatalf("Assistant contract must not pretend registry tools are manifest capabilities: %#v", contract.CapabilityNames)
}
if !contract.Affordances.Tools || !contract.Affordances.InteractiveQuestions || contract.Affordances.Resources {
t.Fatalf("Assistant affordances = %+v", contract.Affordances)
}
legacy, ok := ProjectPulseAssistantSurfaceToolContract(SurfaceContract{}, []ProviderTool{
NewPulseQuestionProviderTool(),
})
if ok {
t.Fatal("legacy Assistant surface fallback must not report a declared surface")
}
if legacy.SurfaceID != SurfaceIDPulseAssistant || legacy.SurfaceLabel != "Pulse Assistant" {
t.Fatalf("legacy Assistant surface identity = %+v", legacy)
}
if !legacy.Affordances.Tools || !legacy.Affordances.InteractiveQuestions || legacy.Affordances.Resources {
t.Fatalf("legacy Assistant affordances = %+v", legacy.Affordances)
}
}
func TestProjectPulseAssistantSurfaceToolContractHonorsSurfaceAffordances(t *testing.T) {
providerTools := []ProviderTool{
{Name: PulseQueryToolName},
NewPulseQuestionProviderTool(),
}
toolsDisabled := CanonicalManifest().SurfaceContract
for i := range toolsDisabled.OperatorSurfaces {
if toolsDisabled.OperatorSurfaces[i].ID == SurfaceIDPulseAssistant {
toolsDisabled.OperatorSurfaces[i].Affordances = SurfaceAffordanceContract{InteractiveQuestions: true}
}
}
withoutTools, _ := ProjectPulseAssistantSurfaceToolContract(toolsDisabled, providerTools)
if len(withoutTools.ToolNames) != 0 || len(withoutTools.RegistryToolNames) != 0 || len(withoutTools.NativeToolNames) != 0 {
t.Fatalf("tools-disabled Assistant surface contract = %+v, want no published tools", withoutTools)
}
questionsDisabled := CanonicalManifest().SurfaceContract
for i := range questionsDisabled.OperatorSurfaces {
if questionsDisabled.OperatorSurfaces[i].ID == SurfaceIDPulseAssistant {
questionsDisabled.OperatorSurfaces[i].Affordances = SurfaceAffordanceContract{Tools: true}
}
}
withoutQuestions, _ := ProjectPulseAssistantSurfaceToolContract(questionsDisabled, providerTools)
if !reflect.DeepEqual(withoutQuestions.ToolNames, []string{PulseQueryToolName}) {
t.Fatalf("questions-disabled Assistant tool names = %#v, want registry tool only", withoutQuestions.ToolNames)
}
if len(withoutQuestions.NativeToolNames) != 0 {
t.Fatalf("questions-disabled Assistant native names = %#v, want none", withoutQuestions.NativeToolNames)
}
if !reflect.DeepEqual(withoutQuestions.RegistryToolNames, []string{PulseQueryToolName}) {
t.Fatalf("questions-disabled Assistant registry names = %#v", withoutQuestions.RegistryToolNames)
}
}
func TestIntersectSurfaceAffordancesCannotEnableDisabledSurfaceAffordance(t *testing.T) {
allowed := SurfaceAffordanceContract{
Resources: true,
Prompts: true,
CapabilityMetadata: true,
}
requested := SurfaceAffordanceContract{
Tools: true,
Resources: true,
Prompts: true,
CapabilityMetadata: true,
}
got := IntersectSurfaceAffordances(allowed, requested)
if got.Tools {
t.Fatalf("intersection re-enabled disabled tools affordance: %+v", got)
}
if !got.Resources || !got.Prompts || !got.CapabilityMetadata {
t.Fatalf("intersection dropped allowed requested affordances: %+v", got)
}
if got.InteractiveQuestions {
t.Fatalf("intersection enabled disabled interactive questions affordance: %+v", got)
}
}
func TestProjectManifestSurfaceToolContractUsesPublishedSurfaceToolContract(t *testing.T) {
manifest := Manifest{
SurfaceContract: CanonicalManifest().SurfaceContract,
SurfaceToolContracts: []SurfaceToolContract{{
SurfaceID: SurfaceIDPulseMCP,
ToolSource: SurfaceToolSourceCapabilityManifest,
ToolNames: []string{
SetOperatorStateCapabilityName,
ResourceContextCapabilityName,
EventSubscriptionCapabilityName,
"missing_tool",
SetOperatorStateCapabilityName,
},
}},
Capabilities: []Capability{
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
{Name: EventSubscriptionCapabilityName, Description: "Stream events."},
{Name: ResourceContextCapabilityName, Description: "Read resource context."},
{Name: SetOperatorStateCapabilityName, Description: "Set operator state."},
},
}
contract, ok := ProjectManifestSurfaceToolContract(manifest, SurfaceIDPulseMCP)
if !ok {
t.Fatal("canonical manifest must declare Pulse MCP surface")
}
if contract.SurfaceID != SurfaceIDPulseMCP || contract.SurfaceLabel != "Pulse MCP" {
t.Fatalf("MCP surface identity = %+v", contract)
}
if contract.ToolSource != SurfaceToolSourceCapabilityManifest {
t.Fatalf("MCP tool source = %q, want %s", contract.ToolSource, SurfaceToolSourceCapabilityManifest)
}
want := []string{SetOperatorStateCapabilityName, ResourceContextCapabilityName}
if !reflect.DeepEqual(contract.ToolNames, want) {
t.Fatalf("MCP tool names = %#v, want %#v", contract.ToolNames, want)
}
if !reflect.DeepEqual(contract.CapabilityNames, want) {
t.Fatalf("MCP capability names = %#v, want %#v", contract.CapabilityNames, want)
}
if len(contract.RegistryToolNames) != 0 || len(contract.NativeToolNames) != 0 {
t.Fatalf("MCP contract must not expose Assistant registry/native tools: %+v", contract)
}
if !contract.Affordances.Tools || !contract.Affordances.Resources || !contract.Affordances.Prompts || !contract.Affordances.CapabilityMetadata || contract.Affordances.InteractiveQuestions {
t.Fatalf("MCP affordances = %+v", contract.Affordances)
}
}
func TestProjectManifestSurfaceToolContractCannotReenableDisabledSurfaceTools(t *testing.T) {
surfaceContract := CloneSurfaceContract(CanonicalManifest().SurfaceContract)
for i := range surfaceContract.OperatorSurfaces {
if surfaceContract.OperatorSurfaces[i].ID == SurfaceIDPulseMCP {
surfaceContract.OperatorSurfaces[i].Affordances = SurfaceAffordanceContract{
Resources: true,
Prompts: true,
CapabilityMetadata: true,
}
}
}
manifest := Manifest{
SurfaceContract: surfaceContract,
SurfaceToolContracts: []SurfaceToolContract{{
SurfaceID: SurfaceIDPulseMCP,
ToolSource: SurfaceToolSourceCapabilityManifest,
ToolNames: []string{FleetContextCapabilityName},
Affordances: DefaultSurfaceAffordancesForID(SurfaceIDPulseMCP),
}},
Capabilities: []Capability{
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
},
}
contract, ok := ProjectManifestSurfaceToolContract(manifest, SurfaceIDPulseMCP)
if !ok {
t.Fatal("published Pulse MCP surface tool contract should still resolve")
}
if contract.Affordances.Tools {
t.Fatalf("disabled surface tools affordance was re-enabled: %+v", contract.Affordances)
}
if len(contract.ToolNames) != 0 || len(contract.CapabilityNames) != 0 {
t.Fatalf("tools-disabled external surface exposed tool names: %+v", contract)
}
if !contract.Affordances.Resources || !contract.Affordances.Prompts || !contract.Affordances.CapabilityMetadata {
t.Fatalf("allowed non-tool affordances were lost: %+v", contract.Affordances)
}
}
func TestProjectManifestSurfaceToolContractRequiresPublishedSurfaceToolContract(t *testing.T) {
manifest := Manifest{
SurfaceContract: CanonicalManifest().SurfaceContract,
Capabilities: []Capability{
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
{Name: EventSubscriptionCapabilityName, Description: "Stream events."},
{Name: ResourceContextCapabilityName, Description: "Read resource context."},
},
}
if contract, ok := ProjectManifestSurfaceToolContract(manifest, SurfaceIDPulseMCP); ok {
t.Fatalf("missing Pulse MCP surface tool contract must fail closed, got %+v", contract)
}
if contracts := ProjectManifestSurfaceToolContracts(manifest); len(contracts) != 0 {
t.Fatalf("missing external surface tool contract projected tools: %+v", contracts)
}
}
func TestProjectManifestSurfaceToolContractRejectsNativeOrMissingSurfaces(t *testing.T) {
manifest := Manifest{
SurfaceContract: CanonicalManifest().SurfaceContract,
Capabilities: []Capability{
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
},
}
if contract, ok := ProjectManifestSurfaceToolContract(manifest, SurfaceIDPulseAssistant); ok {
t.Fatalf("native Assistant surface must not project static manifest tools: %+v", contract)
}
if contract, ok := ProjectManifestSurfaceToolContract(manifest, "missing_surface"); ok {
t.Fatalf("missing surface must not project static manifest tools: %+v", contract)
}
}
func TestProjectManifestSurfaceToolContractsReturnsExternalAdaptersInManifestOrder(t *testing.T) {
surfaceContract := CanonicalManifest().SurfaceContract
surfaceContract.OperatorSurfaces = append(surfaceContract.OperatorSurfaces, OperatorSurfaceContract{
ID: "pulse_cli_agent",
Label: "Pulse CLI Agent",
ExternalAdapter: true,
Affordances: SurfaceAffordanceContract{
Tools: true,
CapabilityMetadata: true,
},
})
manifest := Manifest{
SurfaceContract: surfaceContract,
SurfaceToolContracts: []SurfaceToolContract{
{
SurfaceID: SurfaceIDPulseMCP,
ToolSource: SurfaceToolSourceCapabilityManifest,
ToolNames: []string{FleetContextCapabilityName},
},
{
SurfaceID: "pulse_cli_agent",
ToolSource: SurfaceToolSourceCapabilityManifest,
ToolNames: []string{PlanActionCapabilityName},
},
},
Capabilities: []Capability{
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
{Name: EventSubscriptionCapabilityName, Description: "Stream events."},
{Name: PlanActionCapabilityName, Description: "Plan a governed action."},
},
}
contracts := ProjectManifestSurfaceToolContracts(manifest)
if len(contracts) != 2 {
t.Fatalf("manifest surface contracts = %+v, want MCP and custom external adapter", contracts)
}
if contracts[0].SurfaceID != SurfaceIDPulseMCP || contracts[1].SurfaceID != "pulse_cli_agent" {
t.Fatalf("manifest external surface order = %+v", contracts)
}
if !reflect.DeepEqual(contracts[0].ToolNames, []string{FleetContextCapabilityName}) {
t.Fatalf("MCP surface tool names = %#v", contracts[0].ToolNames)
}
if !reflect.DeepEqual(contracts[1].ToolNames, []string{PlanActionCapabilityName}) {
t.Fatalf("custom external surface tool names = %#v", contracts[1].ToolNames)
}
for _, contract := range contracts {
if contract.ToolSource != SurfaceToolSourceCapabilityManifest {
t.Fatalf("external surface source = %q", contract.ToolSource)
}
for _, name := range contract.ToolNames {
if name == EventSubscriptionCapabilityName {
t.Fatalf("external surface tool names included streaming capability: %#v", contract.ToolNames)
}
}
}
}
func TestProjectPulseIntelligenceSurfaceToolContractsPreservesManifestSurfaceOrder(t *testing.T) {
surfaceContract := CanonicalManifest().SurfaceContract
surfaceContract.OperatorSurfaces = append(surfaceContract.OperatorSurfaces, OperatorSurfaceContract{
ID: "pulse_cli_agent",
Label: "Pulse CLI Agent",
ExternalAdapter: true,
Affordances: SurfaceAffordanceContract{
Tools: true,
},
})
manifest := Manifest{
SurfaceContract: surfaceContract,
SurfaceToolContracts: []SurfaceToolContract{
{
SurfaceID: SurfaceIDPulseMCP,
ToolSource: SurfaceToolSourceCapabilityManifest,
ToolNames: []string{FleetContextCapabilityName},
},
{
SurfaceID: "pulse_cli_agent",
ToolSource: SurfaceToolSourceCapabilityManifest,
ToolNames: []string{},
},
},
Capabilities: []Capability{
{Name: FleetContextCapabilityName, Description: "Read fleet context."},
{Name: PlanActionCapabilityName, Description: "Plan a governed action."},
{Name: EventSubscriptionCapabilityName, Description: "Stream events."},
},
}
contracts := ProjectPulseIntelligenceSurfaceToolContracts(manifest, []ProviderTool{
{Name: PulseQueryToolName},
NewPulseQuestionProviderTool(),
})
if len(contracts) != 3 {
t.Fatalf("surface contracts = %+v, want Assistant, MCP, and custom external adapter", contracts)
}
if contracts[0].SurfaceID != SurfaceIDPulseAssistant || contracts[1].SurfaceID != SurfaceIDPulseMCP || contracts[2].SurfaceID != "pulse_cli_agent" {
t.Fatalf("surface contract order = %+v", contracts)
}
if !reflect.DeepEqual(contracts[0].ToolNames, []string{PulseQueryToolName, PulseQuestionToolName}) {
t.Fatalf("Assistant projected names = %#v", contracts[0].ToolNames)
}
if !reflect.DeepEqual(contracts[1].ToolNames, []string{FleetContextCapabilityName}) {
t.Fatalf("MCP projected names = %#v", contracts[1].ToolNames)
}
if !reflect.DeepEqual(contracts[2].ToolNames, []string{}) {
t.Fatalf("custom external adapter projected names = %#v", contracts[2].ToolNames)
}
}