Files
pulse/internal/agentcapabilities/manifest_test.go
T
2026-07-13 16:24:06 +01:00

956 lines
37 KiB
Go

package agentcapabilities
import (
"encoding/json"
"net/http"
"os"
"reflect"
"strings"
"testing"
"github.com/rcourtman/pulse-go-rewrite/pkg/auth"
)
func TestCanonicalManifestOwnsAgentSurface(t *testing.T) {
manifest := CanonicalManifest()
if manifest.Version != "v1" {
t.Fatalf("Version = %q, want v1", manifest.Version)
}
if len(manifest.Capabilities) == 0 {
t.Fatal("CanonicalManifest must declare agent capabilities")
}
for _, name := range []string{
FleetContextCapabilityName,
ResourceContextCapabilityName,
ListResourceCapabilitiesCapabilityName,
OperationsLoopStatusCapabilityName,
SetOperatorStateCapabilityName,
ListFindingsCapabilityName,
PlanActionCapabilityName,
ExecuteActionCapabilityName,
} {
if _, ok := FindCapability(manifest.Capabilities, name); !ok {
t.Fatalf("CanonicalManifest missing %q", name)
}
}
}
func TestCanonicalManifestPinsPatrolControlStatusCapability(t *testing.T) {
if PatrolControlStatusCapabilityName != "get_patrol_control_status" {
t.Fatalf("PatrolControlStatusCapabilityName = %q, want get_patrol_control_status", PatrolControlStatusCapabilityName)
}
if PatrolControlStatusCapabilityPath != "/api/agent/patrol-control/status" {
t.Fatalf("PatrolControlStatusCapabilityPath = %q, want /api/agent/patrol-control/status", PatrolControlStatusCapabilityPath)
}
if OperationsLoopStatusCapabilityName != PatrolControlStatusCapabilityName {
t.Fatalf("OperationsLoopStatusCapabilityName must remain a source-compatible alias for %q", PatrolControlStatusCapabilityName)
}
if OperationsLoopStatusCapabilityPath != PatrolControlStatusCapabilityPath {
t.Fatalf("OperationsLoopStatusCapabilityPath must remain a source-compatible alias for %q", PatrolControlStatusCapabilityPath)
}
if OperationsLoopStatusCompatibilityPath != "/api/agent/operations-loop/status" {
t.Fatalf("OperationsLoopStatusCompatibilityPath = %q, want /api/agent/operations-loop/status", OperationsLoopStatusCompatibilityPath)
}
manifest := CanonicalManifest()
cap, ok := FindCapability(manifest.Capabilities, PatrolControlStatusCapabilityName)
if !ok {
t.Fatalf("CanonicalManifest missing %q", PatrolControlStatusCapabilityName)
}
if cap.Path != PatrolControlStatusCapabilityPath {
t.Fatalf("%s path = %q, want %q", PatrolControlStatusCapabilityName, cap.Path, PatrolControlStatusCapabilityPath)
}
if _, ok := FindCapability(manifest.Capabilities, "get_operations_loop_status"); ok {
t.Fatal("CanonicalManifest must not publish legacy get_operations_loop_status as a primary capability")
}
}
func TestCanonicalManifestUsesSharedResourceContextAddressing(t *testing.T) {
if ResourceIDArgumentName != "resourceId" {
t.Fatalf("ResourceIDArgumentName = %q, want resourceId", ResourceIDArgumentName)
}
manifest := CanonicalManifest()
for name, path := range map[string]string{
FleetContextCapabilityName: FleetContextCapabilityPath,
ResourceContextCapabilityName: ResourceContextCapabilityPath,
ListResourceCapabilitiesCapabilityName: ListResourceCapabilitiesCapabilityPath,
OperationsLoopStatusCapabilityName: OperationsLoopStatusCapabilityPath,
GetOperatorStateCapabilityName: OperatorStateCapabilityPath,
SetOperatorStateCapabilityName: OperatorStateCapabilityPath,
ClearOperatorStateCapabilityName: OperatorStateCapabilityPath,
} {
cap, ok := FindCapability(manifest.Capabilities, name)
if !ok {
t.Fatalf("CanonicalManifest missing %q", name)
}
if cap.Path != path {
t.Fatalf("%s path = %q, want %q", name, cap.Path, path)
}
}
for _, name := range []string{GetOperatorStateCapabilityName, SetOperatorStateCapabilityName, ClearOperatorStateCapabilityName, PlanActionCapabilityName} {
cap, ok := FindCapability(manifest.Capabilities, name)
if !ok {
t.Fatalf("CanonicalManifest missing %q", name)
}
var schema map[string]any
if err := json.Unmarshal(ToolInputSchema(cap), &schema); err != nil {
t.Fatalf("%s projected inputSchema must be valid JSON: %v", name, err)
}
properties, _ := schema["properties"].(map[string]any)
if _, ok := properties[ResourceIDArgumentName]; !ok {
t.Fatalf("%s projected inputSchema properties missing %q: %v", name, ResourceIDArgumentName, properties)
}
if !schemaRequiredContains(schema, ResourceIDArgumentName) {
t.Fatalf("%s projected inputSchema required = %v, want %q", name, schema["required"], ResourceIDArgumentName)
}
}
}
func TestCanonicalManifestUsesSharedOperatorStateVocabulary(t *testing.T) {
for name, want := range map[string]string{
GetOperatorStateCapabilityName: "get_operator_state",
SetOperatorStateCapabilityName: "set_operator_state",
ClearOperatorStateCapabilityName: "clear_operator_state",
} {
if name != want {
t.Fatalf("operator-state capability constant = %q, want %q", name, want)
}
}
manifest := CanonicalManifest()
for _, name := range []string{
GetOperatorStateCapabilityName,
SetOperatorStateCapabilityName,
ClearOperatorStateCapabilityName,
} {
cap, ok := FindCapability(manifest.Capabilities, name)
if !ok {
t.Fatalf("CanonicalManifest missing %q", name)
}
if cap.Path != OperatorStateCapabilityPath {
t.Fatalf("%s path = %q, want %q", name, cap.Path, OperatorStateCapabilityPath)
}
}
}
func TestCanonicalManifestUsesSharedFindingVocabulary(t *testing.T) {
if FindingIDArgumentName != "finding_id" {
t.Fatalf("FindingIDArgumentName = %q, want finding_id", FindingIDArgumentName)
}
if ResolutionNoteArgumentName != "resolution_note" {
t.Fatalf("ResolutionNoteArgumentName = %q, want resolution_note", ResolutionNoteArgumentName)
}
if NoteArgumentName != "note" {
t.Fatalf("NoteArgumentName = %q, want note", NoteArgumentName)
}
manifest := CanonicalManifest()
for _, name := range []string{
ListFindingsCapabilityName,
AcknowledgeFindingCapabilityName,
SnoozeFindingCapabilityName,
DismissFindingCapabilityName,
ResolveFindingCapabilityName,
} {
if _, ok := FindCapability(manifest.Capabilities, name); !ok {
t.Fatalf("CanonicalManifest missing %q", name)
}
}
for _, name := range []string{
AcknowledgeFindingCapabilityName,
SnoozeFindingCapabilityName,
DismissFindingCapabilityName,
ResolveFindingCapabilityName,
} {
cap, ok := FindCapability(manifest.Capabilities, name)
if !ok {
t.Fatalf("CanonicalManifest missing %q", name)
}
var schema map[string]any
if err := json.Unmarshal(cap.InputSchema, &schema); err != nil {
t.Fatalf("%s inputSchema must be valid JSON: %v", name, err)
}
properties, _ := schema["properties"].(map[string]any)
if _, ok := properties[FindingIDArgumentName]; !ok {
t.Fatalf("%s inputSchema properties missing %q: %v", name, FindingIDArgumentName, properties)
}
if !schemaRequiredContains(schema, FindingIDArgumentName) {
t.Fatalf("%s inputSchema required = %v, want %q", name, schema["required"], FindingIDArgumentName)
}
}
resolve, ok := FindCapability(manifest.Capabilities, ResolveFindingCapabilityName)
if !ok {
t.Fatalf("CanonicalManifest missing %q", ResolveFindingCapabilityName)
}
var resolveSchema map[string]any
if err := json.Unmarshal(resolve.InputSchema, &resolveSchema); err != nil {
t.Fatalf("%s inputSchema must be valid JSON: %v", ResolveFindingCapabilityName, err)
}
resolveProps, _ := resolveSchema["properties"].(map[string]any)
if _, ok := resolveProps[ResolutionNoteArgumentName]; !ok {
t.Fatalf("%s inputSchema properties missing %q: %v", ResolveFindingCapabilityName, ResolutionNoteArgumentName, resolveProps)
}
if schemaRequiredContains(resolveSchema, ResolutionNoteArgumentName) {
t.Fatalf("%s inputSchema required = %v, %q must remain optional", ResolveFindingCapabilityName, resolveSchema["required"], ResolutionNoteArgumentName)
}
dismiss, ok := FindCapability(manifest.Capabilities, DismissFindingCapabilityName)
if !ok {
t.Fatalf("CanonicalManifest missing %q", DismissFindingCapabilityName)
}
var dismissSchema map[string]any
if err := json.Unmarshal(dismiss.InputSchema, &dismissSchema); err != nil {
t.Fatalf("%s inputSchema must be valid JSON: %v", DismissFindingCapabilityName, err)
}
dismissProps, _ := dismissSchema["properties"].(map[string]any)
if _, ok := dismissProps[NoteArgumentName]; !ok {
t.Fatalf("%s inputSchema properties missing %q: %v", DismissFindingCapabilityName, NoteArgumentName, dismissProps)
}
if schemaRequiredContains(dismissSchema, NoteArgumentName) {
t.Fatalf("%s inputSchema required = %v, %q must remain optional", DismissFindingCapabilityName, dismissSchema["required"], NoteArgumentName)
}
}
func TestCanonicalManifestUsesSharedActionVocabulary(t *testing.T) {
for name, want := range map[string]string{
RequestIDArgumentName: "requestId",
ActionIDArgumentName: "actionId",
CapabilityNameArgumentName: "capabilityName",
ReasonArgumentName: "reason",
RequestedByArgumentName: "requestedBy",
OutcomeArgumentName: "outcome",
} {
if name != want {
t.Fatalf("action argument constant = %q, want %q", name, want)
}
}
manifest := CanonicalManifest()
for name, path := range map[string]string{
PlanActionCapabilityName: PlanActionCapabilityPath,
DecideActionCapabilityName: ActionDecisionCapabilityPath,
ExecuteActionCapabilityName: ActionExecutionCapabilityPath,
} {
cap, ok := FindCapability(manifest.Capabilities, name)
if !ok {
t.Fatalf("CanonicalManifest missing %q", name)
}
if cap.Path != path {
t.Fatalf("%s path = %q, want %q", name, cap.Path, path)
}
}
for name, required := range map[string][]string{
PlanActionCapabilityName: {RequestIDArgumentName, ResourceIDArgumentName, CapabilityNameArgumentName, ReasonArgumentName, RequestedByArgumentName},
DecideActionCapabilityName: {ActionIDArgumentName, OutcomeArgumentName},
ExecuteActionCapabilityName: {ActionIDArgumentName},
} {
cap, ok := FindCapability(manifest.Capabilities, name)
if !ok {
t.Fatalf("CanonicalManifest missing %q", name)
}
var schema map[string]any
if err := json.Unmarshal(cap.InputSchema, &schema); err != nil {
t.Fatalf("%s inputSchema must be valid JSON: %v", name, err)
}
properties, _ := schema["properties"].(map[string]any)
for _, field := range required {
if _, ok := properties[field]; !ok {
t.Fatalf("%s inputSchema properties missing %q: %v", name, field, properties)
}
if !schemaRequiredContains(schema, field) {
t.Fatalf("%s inputSchema required = %v, want %q", name, schema["required"], field)
}
}
}
}
func TestCanonicalManifestPinsPulseIntelligenceSurfaceContract(t *testing.T) {
manifest := CanonicalManifest()
contract := manifest.SurfaceContract
if contract.Core.ID != "pulse_intelligence_core" || contract.Core.Label != "Pulse Intelligence Core" {
t.Fatalf("manifest core contract = %+v", contract.Core)
}
if !strings.Contains(contract.Core.Description, "Canonical context, governed actions, safety gates, approval state, action audit, and verification") {
t.Fatalf("manifest core description does not pin shared core ownership: %q", contract.Core.Description)
}
if contract.ProactiveEngine.ID != "pulse_patrol" || contract.ProactiveEngine.Label != "Pulse Patrol" {
t.Fatalf("manifest Patrol operator component = %+v", contract.ProactiveEngine)
}
if !strings.Contains(contract.ProactiveEngine.Description, "first-party operations surface") ||
!strings.Contains(contract.ProactiveEngine.Description, "Patrol mode") ||
!strings.Contains(contract.ProactiveEngine.Description, "verifies outcomes") {
t.Fatalf("manifest Patrol description must describe Patrol as the primary built-in operator, got %q", contract.ProactiveEngine.Description)
}
if len(contract.OperatorSurfaces) != 2 {
t.Fatalf("access path count = %d, want Assistant and MCP", len(contract.OperatorSurfaces))
}
surfaces := map[string]OperatorSurfaceContract{}
for _, surface := range contract.OperatorSurfaces {
surfaces[surface.ID] = surface
if surface.ID == "pulse_patrol" {
t.Fatal("Pulse Patrol must stay the primary built-in operator component, not be flattened into the compatibility access-path list")
}
}
assistant := surfaces["pulse_assistant"]
if assistant.Label != "Pulse Assistant" || !assistant.Native || assistant.ExternalAdapter {
t.Fatalf("Pulse Assistant surface contract = %+v", assistant)
}
if !strings.Contains(assistant.Description, "contextual explanation") ||
!strings.Contains(assistant.Description, "Patrol findings") {
t.Fatalf("Pulse Assistant description must preserve contextual access-path role, got %q", assistant.Description)
}
if !assistant.Affordances.Tools || !assistant.Affordances.InteractiveQuestions ||
assistant.Affordances.Resources || assistant.Affordances.Prompts || assistant.Affordances.CapabilityMetadata {
t.Fatalf("Pulse Assistant affordance contract = %+v", assistant.Affordances)
}
mcp := surfaces["pulse_mcp"]
if mcp.Label != "Pulse MCP" || mcp.Native || !mcp.ExternalAdapter {
t.Fatalf("Pulse MCP surface contract = %+v", mcp)
}
if !strings.Contains(mcp.Description, "external-agent adapter") {
t.Fatalf("Pulse MCP description must preserve adapter role, got %q", mcp.Description)
}
if !mcp.Affordances.Tools || !mcp.Affordances.Resources || !mcp.Affordances.Prompts || !mcp.Affordances.CapabilityMetadata ||
mcp.Affordances.InteractiveQuestions {
t.Fatalf("Pulse MCP affordance contract = %+v", mcp.Affordances)
}
}
func TestCanonicalManifestPublishesExternalSurfaceToolContracts(t *testing.T) {
manifest := CanonicalManifest()
if len(manifest.SurfaceToolContracts) != 1 {
t.Fatalf("surface tool contracts = %+v, want static Pulse MCP contract only", manifest.SurfaceToolContracts)
}
mcp := manifest.SurfaceToolContracts[0]
if mcp.SurfaceID != SurfaceIDPulseMCP || mcp.ToolSource != SurfaceToolSourceCapabilityManifest {
t.Fatalf("surface tool contract identity = %+v", mcp)
}
if len(mcp.ToolNames) == 0 || !reflect.DeepEqual(mcp.ToolNames, mcp.CapabilityNames) {
t.Fatalf("MCP surface tools must be manifest-published capability names, got tools=%#v capabilities=%#v", mcp.ToolNames, mcp.CapabilityNames)
}
want := canonicalPulseMCPSurfaceToolNames()
if !reflect.DeepEqual(mcp.ToolNames, want) {
t.Fatalf("MCP surface tools = %#v, want explicit canonical allowlist %#v", mcp.ToolNames, want)
}
for _, name := range mcp.ToolNames {
switch name {
case EventSubscriptionCapabilityName, PulseQuestionToolName:
t.Fatalf("static external surface tools must not include %s: %#v", name, mcp.ToolNames)
}
if _, err := ResolveRequestResponseCapability(manifest.Capabilities, name); err != nil {
t.Fatalf("static external surface tool %q must resolve to a request/response manifest capability: %v", name, err)
}
}
if len(mcp.RegistryToolNames) != 0 || len(mcp.NativeToolNames) != 0 {
t.Fatalf("static external surface must not expose Assistant runtime buckets: %+v", mcp)
}
}
func TestCanonicalManifestPinsPulseMCPResolvedOperationsLoopCapabilities(t *testing.T) {
manifest := CanonicalManifest()
contract, ok := FindManifestSurfaceToolContract(manifest, SurfaceIDPulseMCP)
if !ok {
t.Fatal("Pulse MCP must publish a static surface tool contract")
}
if contract.ToolSource != SurfaceToolSourceCapabilityManifest {
t.Fatalf("Pulse MCP tool source = %q, want %q", contract.ToolSource, SurfaceToolSourceCapabilityManifest)
}
surfaceCapabilities := ManifestSurfaceToolCapabilities(manifest, SurfaceIDPulseMCP)
surfaceCapabilityNames := map[string]bool{}
for _, cap := range surfaceCapabilities {
surfaceCapabilityNames[cap.Name] = true
}
surfaceTools := ProjectManifestSurfaceTools(manifest, SurfaceIDPulseMCP)
surfaceToolsByName := map[string]ProjectedTool{}
for _, tool := range surfaceTools {
surfaceToolsByName[tool.Name] = tool
}
requiredLoop := []struct {
name string
category string
method string
path string
scope string
mode ActionMode
approval ApprovalPolicy
errorCodes []string
}{
{
name: FleetContextCapabilityName,
category: "context",
method: http.MethodGet,
path: FleetContextCapabilityPath,
scope: auth.ScopeMonitoringRead,
mode: ActionModeRead,
approval: ApprovalPolicyScopeOnly,
},
{
name: OperationsLoopStatusCapabilityName,
category: "context",
method: http.MethodGet,
path: OperationsLoopStatusCapabilityPath,
scope: auth.ScopeMonitoringRead,
mode: ActionModeRead,
approval: ApprovalPolicyScopeOnly,
},
{
name: ResourceContextCapabilityName,
category: "context",
method: http.MethodGet,
path: ResourceContextCapabilityPath,
scope: auth.ScopeMonitoringRead,
mode: ActionModeRead,
approval: ApprovalPolicyScopeOnly,
errorCodes: []string{AgentErrCodeResourceNotFound},
},
{
name: ListResourceCapabilitiesCapabilityName,
category: "context",
method: http.MethodGet,
path: ListResourceCapabilitiesCapabilityPath,
scope: auth.ScopeMonitoringRead,
mode: ActionModeRead,
approval: ApprovalPolicyScopeOnly,
errorCodes: []string{AgentErrCodeResourceNotFound},
},
{
name: ListFindingsCapabilityName,
category: "finding",
method: http.MethodGet,
path: "/api/ai/patrol/findings",
scope: auth.ScopeAIExecute,
mode: ActionModeRead,
approval: ApprovalPolicyScopeOnly,
},
{
name: ResolveFindingCapabilityName,
category: "finding",
method: http.MethodPost,
path: "/api/ai/patrol/resolve",
scope: auth.ScopeAIExecute,
mode: ActionModeWrite,
approval: ApprovalPolicyScopeOnly,
errorCodes: []string{
AgentErrCodeInvalidFindingRequest,
AgentErrCodeFindingNotFound,
AgentErrCodeFindingActionNotAllowed,
AgentErrCodePatrolUnavailable,
},
},
{
name: PlanActionCapabilityName,
category: "action",
method: http.MethodPost,
path: PlanActionCapabilityPath,
scope: auth.ScopeActionsPlan,
mode: ActionModeWrite,
approval: ApprovalPolicyActionPlan,
errorCodes: []string{
AgentErrCodeInvalidActionRequest,
AgentErrCodeMockModeEnabled,
AgentErrCodeActionActorUnavailable,
AgentErrCodeResourceNotFound,
AgentErrCodeCapabilityNotFound,
AgentErrCodeActionExecutionUnavailable,
},
},
{
name: DecideActionCapabilityName,
category: "action",
method: http.MethodPost,
path: ActionDecisionCapabilityPath,
scope: auth.ScopeActionsApprove,
mode: ActionModeWrite,
approval: ApprovalPolicyActionPlan,
errorCodes: []string{
AgentErrCodeMockModeEnabled,
AgentErrCodeMissingID,
AgentErrCodeInvalidID,
AgentErrCodeInvalidActionDecision,
AgentErrCodeActionNotFound,
AgentErrCodeActionNotPending,
AgentErrCodeActionPlanExpired,
AgentErrCodeActionPlanIdentityMismatch,
AgentErrCodeActionActorUnavailable,
AgentErrCodeActionApprovalForbidden,
AgentErrCodeActionStepUpUnavailable,
AgentErrCodeActionDecisionConflict,
AgentErrCodeActionSeparationRequired,
AgentErrCodeActionReplanRequired,
},
},
{
name: ExecuteActionCapabilityName,
category: "action",
method: http.MethodPost,
path: ActionExecutionCapabilityPath,
scope: auth.ScopeActionsExecute,
mode: ActionModeWrite,
approval: ApprovalPolicyActionPlan,
errorCodes: []string{
AgentErrCodeMockModeEnabled,
AgentErrCodeMissingID,
AgentErrCodeInvalidID,
AgentErrCodeInvalidActionExecution,
AgentErrCodeActionNotFound,
AgentErrCodeActionNotApproved,
AgentErrCodeActionAlreadyExecuting,
AgentErrCodeActionExecutionFinal,
AgentErrCodeActionDryRunOnly,
AgentErrCodeActionPlanExpired,
AgentErrCodeActionPlanDrift,
AgentErrCodeActionPlanIdentityMismatch,
AgentErrCodeResourceRemediationLocked,
AgentErrCodeActionExecutorUnavailable,
AgentErrCodeActionActorUnavailable,
AgentErrCodeActionExecutionForbidden,
AgentErrCodeActionNotExecuting,
AgentErrCodeActionReplanRequired,
},
},
}
for _, want := range requiredLoop {
if !containsString(contract.ToolNames, want.name) {
t.Fatalf("Pulse MCP surface contract missing resolved-operations loop capability %q: %#v", want.name, contract.ToolNames)
}
if !surfaceCapabilityNames[want.name] {
t.Fatalf("Pulse MCP surface capability projection missing %q: %#v", want.name, surfaceCapabilities)
}
tool, ok := surfaceToolsByName[want.name]
if !ok {
t.Fatalf("Pulse MCP tools/list projection missing %q: %#v", want.name, surfaceTools)
}
cap, ok := FindCapability(manifest.Capabilities, want.name)
if !ok {
t.Fatalf("CanonicalManifest missing %q", want.name)
}
if cap.Category != want.category || cap.Method != want.method || cap.Path != want.path || cap.Scope != want.scope {
t.Fatalf("%s manifest route contract = category:%q method:%q path:%q scope:%q, want category:%q method:%q path:%q scope:%q",
want.name, cap.Category, cap.Method, cap.Path, cap.Scope, want.category, want.method, want.path, want.scope)
}
if cap.ActionMode != want.mode || cap.ApprovalPolicy != want.approval {
t.Fatalf("%s governance = %s/%s, want %s/%s", want.name, cap.ActionMode, cap.ApprovalPolicy, want.mode, want.approval)
}
for _, errorCode := range want.errorCodes {
if !containsString(cap.ErrorCodes, errorCode) {
t.Fatalf("%s errorCodes = %#v, missing %q", want.name, cap.ErrorCodes, errorCode)
}
}
meta, ok := tool.Meta[ToolMetaPulseCapabilityKey].(map[string]any)
if !ok {
t.Fatalf("%s projected MCP tool missing Pulse capability metadata: %#v", want.name, tool.Meta)
}
if meta["name"] != want.name || meta["category"] != want.category || meta["scope"] != want.scope {
t.Fatalf("%s projected MCP metadata = %#v, want name/category/scope from manifest", want.name, meta)
}
route, _ := meta["route"].(map[string]any)
if route["method"] != want.method || route["path"] != want.path {
t.Fatalf("%s projected MCP route metadata = %#v, want %s %s", want.name, route, want.method, want.path)
}
governance, _ := meta["governance"].(map[string]any)
if governance["actionMode"] != string(want.mode) || governance["approvalPolicy"] != string(want.approval) {
t.Fatalf("%s projected MCP governance metadata = %#v, want %s/%s", want.name, governance, want.mode, want.approval)
}
}
}
func TestCanonicalManifestProjectsWorkflowPromptsFromCapabilities(t *testing.T) {
manifest := CanonicalManifest()
want := ProjectPulseWorkflowPrompts(manifest.Capabilities)
if len(manifest.WorkflowPrompts) == 0 {
t.Fatal("CanonicalManifest must expose shared Pulse Intelligence workflow prompts")
}
if len(manifest.WorkflowPrompts) != len(want) {
t.Fatalf("CanonicalManifest WorkflowPrompts length = %d, want %d", len(manifest.WorkflowPrompts), len(want))
}
gotNames := make([]string, 0, len(manifest.WorkflowPrompts))
wantNames := make([]string, 0, len(want))
for i := range manifest.WorkflowPrompts {
got := manifest.WorkflowPrompts[i]
expected := want[i]
gotNames = append(gotNames, got.Name)
wantNames = append(wantNames, expected.Name)
if got.Name != expected.Name || got.Description != expected.Description {
t.Fatalf("workflow prompt[%d] = %+v, want %+v", i, got, expected)
}
if len(got.Arguments) != len(expected.Arguments) {
t.Fatalf("workflow prompt[%d] argument count = %d, want %d", i, len(got.Arguments), len(expected.Arguments))
}
}
for _, name := range []string{
PulseWorkflowPromptTriageFleet,
PulseWorkflowPromptInvestigateResource,
PulseWorkflowPromptReviewFinding,
PulseWorkflowPromptOperationsLoop,
} {
if !containsString(gotNames, name) {
t.Fatalf("CanonicalManifest workflow prompts = %v, missing %q; projected prompts = %v", gotNames, name, wantNames)
}
}
}
func TestCanonicalManifestReturnsDetachedCopy(t *testing.T) {
first := CanonicalManifest()
if len(first.Capabilities) == 0 {
t.Fatal("CanonicalManifest must declare capabilities")
}
first.Version = "mutated"
first.SurfaceContract.Core.Label = "mutated"
if len(first.SurfaceContract.OperatorSurfaces) == 0 {
t.Fatal("CanonicalManifest must declare operator surfaces")
}
first.SurfaceContract.OperatorSurfaces[0].Label = "mutated"
if len(first.SurfaceToolContracts) == 0 {
t.Fatal("CanonicalManifest must declare static external surface tool contracts")
}
if len(first.SurfaceToolContracts[0].ToolNames) == 0 || len(first.SurfaceToolContracts[0].CapabilityNames) == 0 {
t.Fatal("CanonicalManifest must declare static external surface tool names")
}
first.SurfaceToolContracts[0].ToolNames[0] = "mutated"
first.SurfaceToolContracts[0].CapabilityNames[0] = "mutated"
first.MCPAdapter.Command = "mutated"
if len(first.MCPAdapter.ConfigFamilies) == 0 {
t.Fatal("CanonicalManifest must declare MCP adapter config families")
}
first.MCPAdapter.ConfigFamilies[0].Label = "mutated"
if len(first.MCPAdapter.ConfigFamilies[0].FileHints) > 0 {
first.MCPAdapter.ConfigFamilies[0].FileHints[0] = "mutated"
}
if len(first.MCPAdapter.ConfigFamilies[0].ClientLabels) > 0 {
first.MCPAdapter.ConfigFamilies[0].ClientLabels[0] = "mutated"
}
first.Capabilities[0].Name = "mutated"
if len(first.Capabilities[0].ErrorCodes) > 0 {
first.Capabilities[0].ErrorCodes[0] = "mutated"
}
if len(first.RequiredScopes) == 0 {
t.Fatal("CanonicalManifest must declare required scopes")
}
first.RequiredScopes[0] = "mutated"
if len(first.Categories) == 0 {
t.Fatal("CanonicalManifest must declare capability categories")
}
first.Categories[0].Label = "mutated"
if len(first.WorkflowPrompts) == 0 {
t.Fatal("CanonicalManifest must declare workflow prompts")
}
first.WorkflowPrompts[0].Name = "mutated"
workflowPromptArgumentIndex := -1
for i := range first.WorkflowPrompts {
if len(first.WorkflowPrompts[i].Arguments) > 0 {
workflowPromptArgumentIndex = i
first.WorkflowPrompts[i].Arguments[0].Name = "mutated"
break
}
}
if workflowPromptArgumentIndex < 0 {
t.Fatal("CanonicalManifest must declare at least one workflow prompt argument")
}
schemaIndex := -1
for i := range first.Capabilities {
if len(first.Capabilities[i].InputSchema) > 0 {
schemaIndex = i
first.Capabilities[i].InputSchema[0] = '['
break
}
}
if schemaIndex < 0 {
t.Fatal("CanonicalManifest must include at least one authored input schema")
}
second := CanonicalManifest()
if second.Version != "v1" {
t.Fatalf("CanonicalManifest version was mutated: %q", second.Version)
}
if second.SurfaceContract.Core.Label == "mutated" {
t.Fatal("CanonicalManifest returned aliased surface contract component")
}
if second.SurfaceContract.OperatorSurfaces[0].Label == "mutated" {
t.Fatal("CanonicalManifest returned aliased operator surfaces")
}
if second.SurfaceToolContracts[0].ToolNames[0] == "mutated" {
t.Fatal("CanonicalManifest returned aliased surface tool contract tool names")
}
if second.SurfaceToolContracts[0].CapabilityNames[0] == "mutated" {
t.Fatal("CanonicalManifest returned aliased surface tool contract capability names")
}
if second.MCPAdapter.Command == "mutated" {
t.Fatal("CanonicalManifest returned aliased MCP adapter contract")
}
if second.MCPAdapter.ConfigFamilies[0].Label == "mutated" {
t.Fatal("CanonicalManifest returned aliased MCP adapter config families")
}
if len(second.MCPAdapter.ConfigFamilies[0].FileHints) > 0 && second.MCPAdapter.ConfigFamilies[0].FileHints[0] == "mutated" {
t.Fatal("CanonicalManifest returned aliased MCP adapter config-family file hints")
}
if len(second.MCPAdapter.ConfigFamilies[0].ClientLabels) > 0 && second.MCPAdapter.ConfigFamilies[0].ClientLabels[0] == "mutated" {
t.Fatal("CanonicalManifest returned aliased MCP adapter config-family client labels")
}
if second.Capabilities[0].Name == "mutated" {
t.Fatal("CanonicalManifest returned aliased capability slice")
}
if len(first.Capabilities[0].ErrorCodes) > 0 && second.Capabilities[0].ErrorCodes[0] == "mutated" {
t.Fatal("CanonicalManifest returned aliased error code slice")
}
if second.RequiredScopes[0] == "mutated" {
t.Fatal("CanonicalManifest returned aliased required scope slice")
}
if second.Categories[0].Label == "mutated" {
t.Fatal("CanonicalManifest returned aliased capability category slice")
}
if second.WorkflowPrompts[0].Name == "mutated" {
t.Fatal("CanonicalManifest returned aliased workflow prompt slice")
}
if second.WorkflowPrompts[workflowPromptArgumentIndex].Arguments[0].Name == "mutated" {
t.Fatal("CanonicalManifest returned aliased workflow prompt argument slice")
}
if string(second.Capabilities[schemaIndex].InputSchema) == string(first.Capabilities[schemaIndex].InputSchema) {
t.Fatal("CanonicalManifest returned aliased input schema")
}
}
func TestCanonicalManifestPinsMCPAdapterSetupContract(t *testing.T) {
adapter := CanonicalManifest().MCPAdapter
if adapter.ServerName != DefaultMCPAdapterServerName {
t.Fatalf("MCP adapter server name = %q, want %q", adapter.ServerName, DefaultMCPAdapterServerName)
}
if adapter.Command != DefaultMCPAdapterCommand {
t.Fatalf("MCP adapter command = %q, want %q", adapter.Command, DefaultMCPAdapterCommand)
}
if adapter.BaseURLFlag != DefaultMCPAdapterBaseURLFlag {
t.Fatalf("MCP adapter base URL flag = %q, want %q", adapter.BaseURLFlag, DefaultMCPAdapterBaseURLFlag)
}
if adapter.DefaultBaseURL != DefaultMCPAdapterDefaultBaseURL {
t.Fatalf("MCP adapter default base URL = %q, want %q", adapter.DefaultBaseURL, DefaultMCPAdapterDefaultBaseURL)
}
if adapter.TokenEnv != DefaultMCPAdapterTokenEnv {
t.Fatalf("MCP adapter token env = %q, want %q", adapter.TokenEnv, DefaultMCPAdapterTokenEnv)
}
families := map[string]MCPAdapterConfigFamily{}
for _, family := range adapter.ConfigFamilies {
if family.ID == "" || family.Label == "" || family.Shape == "" {
t.Fatalf("MCP adapter config family must declare id, label, and shape: %+v", family)
}
families[family.Shape] = family
}
for _, shape := range []string{
MCPAdapterConfigShapeOpenCodeMCP,
MCPAdapterConfigShapeMCPServers,
MCPAdapterConfigShapeCustom,
} {
if _, ok := families[shape]; !ok {
t.Fatalf("MCP adapter config family shape %q missing from manifest contract", shape)
}
}
opencode := families[MCPAdapterConfigShapeOpenCodeMCP]
if opencode.Label != "OpenCode" || !containsString(opencode.FileHints, "opencode.json") {
t.Fatalf("OpenCode MCP adapter family = %+v", opencode)
}
mcpServers := families[MCPAdapterConfigShapeMCPServers]
if !containsString(mcpServers.ClientLabels, "Claude Desktop") || !containsString(mcpServers.ClientLabels, "Claude Code") {
t.Fatalf("mcpServers adapter family must cover Claude Desktop and Claude Code, got %+v", mcpServers)
}
custom := families[MCPAdapterConfigShapeCustom]
if custom.Label != "custom MCP clients" {
t.Fatalf("custom MCP adapter family = %+v", custom)
}
}
func TestCanonicalManifestDeclaresCategoryPresentation(t *testing.T) {
manifest := CanonicalManifest()
if len(manifest.Categories) == 0 {
t.Fatal("CanonicalManifest must declare category presentation metadata")
}
byID := map[string]CapabilityCategory{}
for _, category := range manifest.Categories {
if category.ID == "" {
t.Fatal("category id must not be empty")
}
if category.Label == "" {
t.Fatalf("category %q must declare a label", category.ID)
}
if category.Description == "" {
t.Fatalf("category %q must declare a description", category.ID)
}
if _, exists := byID[category.ID]; exists {
t.Fatalf("duplicate category id %q", category.ID)
}
byID[category.ID] = category
}
for _, cap := range manifest.Capabilities {
if _, ok := byID[cap.Category]; !ok {
t.Fatalf("capability %q category %q missing from manifest category metadata", cap.Name, cap.Category)
}
}
}
func TestCanonicalManifestDeclaresCapabilityTitles(t *testing.T) {
for _, cap := range CanonicalManifest().Capabilities {
if strings.TrimSpace(cap.Title) == "" {
t.Fatalf("capability %q must declare a human-readable title", cap.Name)
}
if strings.EqualFold(strings.TrimSpace(cap.Title), strings.TrimSpace(cap.Name)) {
t.Fatalf("capability %q title must be display copy, got %q", cap.Name, cap.Title)
}
}
}
func TestCanonicalManifestDeclaresStructuredOutputSchemasForJSONResponses(t *testing.T) {
manifest := CanonicalManifest()
byName := map[string]Capability{}
for _, cap := range manifest.Capabilities {
byName[cap.Name] = cap
if !IsRequestResponseCapability(cap) || strings.TrimSpace(cap.ResponseShape) == "" {
continue
}
if len(cap.OutputSchema) == 0 {
t.Errorf("capability %q responseShape %q must declare outputSchema for structured tool results", cap.Name, cap.ResponseShape)
continue
}
var schema map[string]any
if err := json.Unmarshal(cap.OutputSchema, &schema); err != nil {
t.Errorf("capability %q outputSchema must be valid JSON: %v", cap.Name, err)
continue
}
if schema["type"] != "object" {
t.Errorf("capability %q outputSchema type = %v, want object", cap.Name, schema["type"])
}
props, _ := schema["properties"].(map[string]any)
if strings.HasSuffix(strings.TrimSpace(cap.ResponseShape), "[]") {
if _, ok := props["items"]; !ok {
t.Errorf("array-returning capability %q must declare items for the structured content wrapper: %v", cap.Name, props)
}
if _, ok := props["count"]; !ok {
t.Errorf("array-returning capability %q must declare count for the structured content wrapper: %v", cap.Name, props)
}
if !schemaRequiredContains(schema, "items") || !schemaRequiredContains(schema, "count") {
t.Errorf("array-returning capability %q outputSchema required = %v, want items and count", cap.Name, schema["required"])
}
}
}
for name, fragments := range map[string][]string{
"list_nodes": {`"items"`, `"count"`},
"discover_lan": {`"servers"`, `"structured_errors"`, `"cached"`},
"test_node_credentials": {`"status"`, `"message"`},
"refresh_node_cluster_membership": {`"clusterNodes"`, `"nodesAdded"`},
ListFindingsCapabilityName: {`"items"`, `"count"`},
AcknowledgeFindingCapabilityName: {`"success"`, `"message"`},
OperationsLoopStatusCapabilityName: {
`"steps"`,
`Four-stage Patrol, Assistant, governance, and verification status rollup`,
`Assistant counts contextual collaboration`,
`External-agent readiness is exposed separately through externalAgentReady`,
`Count-only evidence that the Patrol control journey reached a verified Patrol work result`,
`Compatibility alias for patrolControlResolvedOperationsLoopCount`,
`one non-expired API token covers every scope required by the published Pulse MCP Patrol work capability set`,
},
PlanActionCapabilityName: {`"actionId"`, `"planHash"`},
DecideActionCapabilityName: {`"approval"`, `"audit"`},
ExecuteActionCapabilityName: {`"result"`, `"audit"`},
} {
cap, ok := byName[name]
if !ok {
t.Fatalf("manifest missing capability %q", name)
}
raw, err := json.Marshal(cap.OutputSchema)
if err != nil {
t.Fatalf("%s outputSchema marshal: %v", name, err)
}
text := string(raw)
for _, fragment := range fragments {
if !strings.Contains(text, fragment) {
t.Errorf("%s outputSchema missing %s: %s", name, fragment, text)
}
}
}
}
func TestCanonicalManifestRequiredScopesReflectCapabilities(t *testing.T) {
manifest := CanonicalManifest()
want := RequiredCapabilityScopes(manifest.Capabilities)
if strings.Join(manifest.RequiredScopes, "\n") != strings.Join(want, "\n") {
t.Fatalf("CanonicalManifest RequiredScopes = %v, want %v", manifest.RequiredScopes, want)
}
}
func TestCanonicalManifestScopesUseAuthVocabulary(t *testing.T) {
known := map[string]bool{}
for _, scope := range auth.AllKnownScopes {
known[scope] = true
}
for _, cap := range CanonicalManifest().Capabilities {
if !known[cap.Scope] {
t.Errorf("capability %q scope = %q, want auth-owned scope vocabulary", cap.Name, cap.Scope)
}
}
}
func TestCanonicalManifestScopeDeclarationsStayPinnedToAuthConstants(t *testing.T) {
source, err := os.ReadFile("manifest.go")
if err != nil {
t.Fatalf("read manifest.go: %v", err)
}
text := string(source)
for _, required := range []string{
`agentCapabilityScopeMonitoringRead = auth.ScopeMonitoringRead`,
`agentCapabilityScopeMonitoringWrite = auth.ScopeMonitoringWrite`,
`agentCapabilityScopeSettingsRead = auth.ScopeSettingsRead`,
`agentCapabilityScopeSettingsWrite = auth.ScopeSettingsWrite`,
`agentCapabilityScopeAIExecute = auth.ScopeAIExecute`,
} {
if !strings.Contains(text, required) {
t.Fatalf("agent capabilities manifest must declare scope aliases from pkg/auth; missing %s", required)
}
}
if strings.Contains(text, `Scope: "`) || strings.Contains(text, `Scope: "`) {
t.Fatal("agent capabilities manifest scopes must use auth-owned constants, not literal strings")
}
}
func schemaRequiredContains(schema map[string]any, want string) bool {
required, _ := schema["required"].([]any)
for _, raw := range required {
if raw == want {
return true
}
}
return false
}
func containsString(values []string, want string) bool {
for _, value := range values {
if value == want {
return true
}
}
return false
}