diff --git a/cmd/blueclaw-posix-helper/directory_service_test.go b/cmd/blueclaw-posix-helper/directory_service_test.go index ca6a431b..e6baba11 100644 --- a/cmd/blueclaw-posix-helper/directory_service_test.go +++ b/cmd/blueclaw-posix-helper/directory_service_test.go @@ -49,9 +49,9 @@ func TestCreateUserCommandsUseAShellThatExistsOnMacOS(testInstance *testing.T) { } func TestCreateGroupCommandsCarryTheAllocatedIdentity(testInstance *testing.T) { - commands := createGroupCommands("bc_circle_staff", 100004) + commands := createGroupCommands("bc_circle_member", 100004) - if commands[0][2] != "/Groups/bc_circle_staff" { + if commands[0][2] != "/Groups/bc_circle_member" { testInstance.Fatalf("expected the record to be created first, got %v", commands[0]) } requireAttribute(testInstance, commands, "PrimaryGroupID", "100004") diff --git a/cmd/blueclaw-posix-helper/main_test.go b/cmd/blueclaw-posix-helper/main_test.go index 80cd079c..eee86966 100644 --- a/cmd/blueclaw-posix-helper/main_test.go +++ b/cmd/blueclaw-posix-helper/main_test.go @@ -48,9 +48,9 @@ func TestLoadPOSIXStatePrefersStateDocument(t *testing.T) { statePath := filepath.Join(rootPath, "state.json") stateDocument, errorValue := json.Marshal(security.POSIXState{ Directories: []security.POSIXDirectory{{ - Path: "/workspace/circles/staff/sites", + Path: "/workspace/circles/member/sites", Owner: "blueclaw", - Group: "bc_circle_staff", + Group: "bc_circle_member", ModeText: "2770", }}, }) @@ -65,7 +65,7 @@ func TestLoadPOSIXStatePrefersStateDocument(t *testing.T) { if errorValue != nil { t.Fatal(errorValue) } - if len(state.Directories) != 1 || state.Directories[0].Path != "/workspace/circles/staff/sites" { + if len(state.Directories) != 1 || state.Directories[0].Path != "/workspace/circles/member/sites" { t.Fatalf("expected state document to be loaded, got %+v", state.Directories) } } diff --git a/internal/access/access_test.go b/internal/access/access_test.go index 4ed37231..2dfc1b9b 100644 --- a/internal/access/access_test.go +++ b/internal/access/access_test.go @@ -6,17 +6,17 @@ import ( "github.com/yeomyeonggeori/blueclaw/internal/policy" ) -func TestStaffCanAccessStaffCircleFile(t *testing.T) { - personAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}} - resource := "file:circle:staff" +func TestMemberCanAccessMemberCircleFile(t *testing.T) { + personAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}} + resource := "file:circle:member" if !CanAccess(Request{PersonAccess: personAccess, Action: ActionWrite, Resource: resource}) { - t.Fatal("staff should write staff circle files") + t.Fatal("a member should write member circle files") } } func TestCircleMemberCanAccessCircleFile(t *testing.T) { - personAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff", "finance"}} + personAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"member", "finance"}} resource := "file:circle:finance" if !CanAccess(Request{PersonAccess: personAccess, Action: ActionRead, Resource: resource}) { @@ -25,7 +25,7 @@ func TestCircleMemberCanAccessCircleFile(t *testing.T) { } func TestCircleNonMemberCannotAccessCircleFile(t *testing.T) { - personAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}} + personAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}} resource := "file:circle:finance" if CanAccess(Request{PersonAccess: personAccess, Action: ActionRead, Resource: resource}) { @@ -34,8 +34,8 @@ func TestCircleNonMemberCannotAccessCircleFile(t *testing.T) { } func TestPrivateFileOnlyAllowsOwner(t *testing.T) { - ownerAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}} - otherAccess := policy.PersonAccess{PersonID: "person-2", Circles: []string{"staff"}} + ownerAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}} + otherAccess := policy.PersonAccess{PersonID: "person-2", Circles: []string{"member"}} resource := "file:private:person-1" if !CanAccess(Request{PersonAccess: ownerAccess, Action: ActionRead, Resource: resource}) { @@ -52,50 +52,50 @@ func TestRepresentativeToolPolicy(t *testing.T) { Actions: []string{ActionExecute}, Circles: []string{"representative"}, }} - representativeAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff", "representative"}, ResourceAccessRules: resourceAccessRules} - staffAccess := policy.PersonAccess{PersonID: "person-2", Circles: []string{"staff"}, ResourceAccessRules: resourceAccessRules} + representativeAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"member", "representative"}, ResourceAccessRules: resourceAccessRules} + memberAccess := policy.PersonAccess{PersonID: "person-2", Circles: []string{"member"}, ResourceAccessRules: resourceAccessRules} if !CanAccess(Request{PersonAccess: representativeAccess, Action: ActionExecute, Resource: "tool:company_broadcast_send"}) { t.Fatal("representative should execute representative tool") } - if CanAccess(Request{PersonAccess: staffAccess, Action: ActionExecute, Resource: "tool:company_broadcast_send"}) { - t.Fatal("staff should not execute representative tool") + if CanAccess(Request{PersonAccess: memberAccess, Action: ActionExecute, Resource: "tool:company_broadcast_send"}) { + t.Fatal("a member should not execute representative tool") } } func TestFlowResourcePolicies(t *testing.T) { resourceAccessRules := []policy.ResourceAccessPolicy{ - {Resource: "api:flow.summary", Actions: []string{ActionRead}, Circles: []string{"staff"}}, - {Resource: "api:flow.task", Actions: []string{"create", "update"}, Circles: []string{"staff"}}, + {Resource: "api:flow.summary", Actions: []string{ActionRead}, Circles: []string{"member"}}, + {Resource: "api:flow.task", Actions: []string{"create", "update"}, Circles: []string{"member"}}, {Resource: "api:flow.definition", Actions: []string{ActionManage}, Circles: []string{"admin"}}, - {Resource: "tool:task_add", Actions: []string{ActionExecute}, Circles: []string{"staff"}}, - {Resource: "tool:task_list", Actions: []string{ActionExecute}, Circles: []string{"staff"}}, - {Resource: "tool:task_update", Actions: []string{ActionExecute}, Circles: []string{"staff"}}, + {Resource: "tool:task_add", Actions: []string{ActionExecute}, Circles: []string{"member"}}, + {Resource: "tool:task_list", Actions: []string{ActionExecute}, Circles: []string{"member"}}, + {Resource: "tool:task_update", Actions: []string{ActionExecute}, Circles: []string{"member"}}, } - staffAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}, ResourceAccessRules: resourceAccessRules} - adminAccess := policy.PersonAccess{PersonID: "person-2", Circles: []string{"staff", "admin"}, ResourceAccessRules: resourceAccessRules} + memberAccess := policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}, ResourceAccessRules: resourceAccessRules} + adminAccess := policy.PersonAccess{PersonID: "person-2", Circles: []string{"member", "admin"}, ResourceAccessRules: resourceAccessRules} guestAccess := policy.PersonAccess{PersonID: "person-3", ResourceAccessRules: resourceAccessRules} - if !CanAccess(Request{PersonAccess: staffAccess, Action: ActionRead, Resource: "api:flow.summary"}) { - t.Fatal("staff should read Flow summary") + if !CanAccess(Request{PersonAccess: memberAccess, Action: ActionRead, Resource: "api:flow.summary"}) { + t.Fatal("a member should read Flow summary") } - if !CanAccess(Request{PersonAccess: staffAccess, Action: "create", Resource: "api:flow.task"}) { - t.Fatal("staff should create Flow task") + if !CanAccess(Request{PersonAccess: memberAccess, Action: "create", Resource: "api:flow.task"}) { + t.Fatal("a member should create Flow task") } - if CanAccess(Request{PersonAccess: staffAccess, Action: ActionManage, Resource: "api:flow.definition"}) { - t.Fatal("staff should not manage Flow definitions") + if CanAccess(Request{PersonAccess: memberAccess, Action: ActionManage, Resource: "api:flow.definition"}) { + t.Fatal("a member should not manage Flow definitions") } if !CanAccess(Request{PersonAccess: adminAccess, Action: ActionManage, Resource: "api:flow.definition"}) { t.Fatal("admin should manage Flow definitions") } - if !CanAccess(Request{PersonAccess: staffAccess, Action: ActionExecute, Resource: "tool:task_add"}) { - t.Fatal("staff should execute Flow task add tool") + if !CanAccess(Request{PersonAccess: memberAccess, Action: ActionExecute, Resource: "tool:task_add"}) { + t.Fatal("a member should execute Flow task add tool") } - if !CanAccess(Request{PersonAccess: staffAccess, Action: ActionExecute, Resource: "tool:task_list"}) { - t.Fatal("staff should execute Flow task list tool") + if !CanAccess(Request{PersonAccess: memberAccess, Action: ActionExecute, Resource: "tool:task_list"}) { + t.Fatal("a member should execute Flow task list tool") } - if !CanAccess(Request{PersonAccess: staffAccess, Action: ActionExecute, Resource: "tool:task_update"}) { - t.Fatal("staff should execute Flow task update tool") + if !CanAccess(Request{PersonAccess: memberAccess, Action: ActionExecute, Resource: "tool:task_update"}) { + t.Fatal("a member should execute Flow task update tool") } if CanAccess(Request{PersonAccess: guestAccess, Action: ActionExecute, Resource: "tool:task_add"}) { t.Fatal("guest should not execute Flow task add tool") diff --git a/internal/adminapi/policy_handler.go b/internal/adminapi/policy_handler.go index 3905cc5a..89443215 100644 --- a/internal/adminapi/policy_handler.go +++ b/internal/adminapi/policy_handler.go @@ -340,7 +340,7 @@ func createInvitedPersonPolicy(inviteRequest invitePersonRequest, email string) securityLevelName := strings.TrimSpace(inviteRequest.SecurityLevelName) securityLevelRank := inviteRequest.SecurityLevelRank grantedClasses := append([]string{}, inviteRequest.GrantedClasses...) - circles := normalizeCircles(append([]string{policy.StaffCircleID}, inviteRequest.Circles...)) + circles := normalizeCircles(append([]string{policy.MemberCircleID}, inviteRequest.Circles...)) if inviteRequest.IsAdmin { securityLevelName = "admin" securityLevelRank = 100 diff --git a/internal/agentruntime/active_circle_test.go b/internal/agentruntime/active_circle_test.go index 2d37de23..e98044e9 100644 --- a/internal/agentruntime/active_circle_test.go +++ b/internal/agentruntime/active_circle_test.go @@ -9,11 +9,11 @@ import ( func TestResolveActiveCircleIDUsesChannelOrMention(t *testing.T) { channelCircleID, hasChannelConflict := ResolveActiveCircleID(ToolCatalogRequest{ ConversationChannelName: "circle-hr-compensation", - PersonAccess: policy.PersonAccess{Circles: []string{"staff", "hr-compensation"}}, + PersonAccess: policy.PersonAccess{Circles: []string{"member", "hr-compensation"}}, }) mentionedCircleID, hasMentionConflict := ResolveActiveCircleID(ToolCatalogRequest{ Prompt: "please remember this for @hr-compensation", - PersonAccess: policy.PersonAccess{Circles: []string{"staff", "hr-compensation"}}, + PersonAccess: policy.PersonAccess{Circles: []string{"member", "hr-compensation"}}, }) if channelCircleID != "hr-compensation" || hasChannelConflict { @@ -27,7 +27,7 @@ func TestResolveActiveCircleIDUsesChannelOrMention(t *testing.T) { func TestResolveActiveCircleIDIgnoresInaccessibleMention(t *testing.T) { circleID, hasConflict := ResolveActiveCircleID(ToolCatalogRequest{ Prompt: "please remember this for @hr-compensation", - PersonAccess: policy.PersonAccess{Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{Circles: []string{"member"}}, }) if circleID != "" || hasConflict { diff --git a/internal/agentruntime/agent_turn_request_contract_test.go b/internal/agentruntime/agent_turn_request_contract_test.go index 15c351b4..257bace5 100644 --- a/internal/agentruntime/agent_turn_request_contract_test.go +++ b/internal/agentruntime/agent_turn_request_contract_test.go @@ -89,7 +89,7 @@ func TestLaunchedAgentTurnRequestCarriesHostAssembledContext(t *testing.T) { if turnRequest.ToolSet == nil || !containsString(turnRequest.ToolSet.ListToolNames(), "memory_search") { t.Fatalf("expected the launch tool set on the turn request, got %+v", turnRequest.ToolSet) } - if !containsString(turnRequest.RequesterCircles, "staff") { + if !containsString(turnRequest.RequesterCircles, "member") { t.Fatalf("expected resolved requester circles on the turn request, got %+v", turnRequest.RequesterCircles) } } diff --git a/internal/agentruntime/capability_tools_test.go b/internal/agentruntime/capability_tools_test.go index 5d5d5d8c..e2a7d119 100644 --- a/internal/agentruntime/capability_tools_test.go +++ b/internal/agentruntime/capability_tools_test.go @@ -25,7 +25,7 @@ func TestToolCatalogHidesPolicyDeniedCapabilityTools(t *testing.T) { ProfileName: "default", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, ResourceAccessRules: []policy.ResourceAccessPolicy{{ Resource: "tool:site_serve", Actions: []string{"execute"}, @@ -275,22 +275,22 @@ func TestCapabilityToolRequestSeparatesModelInputFromTransport(t *testing.T) { func TestImageReadUsesExactPathInput(t *testing.T) { workspacePath := t.TempDir() - imagePath := filepath.Join(workspacePath, "circles", "staff", "inbox", "mattermost", "thread-1", "post-1", "mascot.png") + imagePath := filepath.Join(workspacePath, "circles", "member", "inbox", "mattermost", "thread-1", "post-1", "mascot.png") writeTestFile(t, imagePath, "image") - httpClient := &recordingHTTPClient{responseBody: `{"provider":"internkim","selectedBackend":"device","toolName":"image_read","outcome":"succeeded","status":"ok","result":{"status":"ok","path":"/workspace/circles/staff/inbox/mattermost/thread-1/post-1/mascot.png","attachments":[{"devicePath":"/workspace/circles/staff/inbox/mattermost/thread-1/post-1/mascot.png","filename":"mascot.png","contentType":"image/png","sizeBytes":5,"contentBase64":"aW1hZ2U="}]}}`} + httpClient := &recordingHTTPClient{responseBody: `{"provider":"internkim","selectedBackend":"device","toolName":"image_read","outcome":"succeeded","status":"ok","result":{"status":"ok","path":"/workspace/circles/member/inbox/mattermost/thread-1/post-1/mascot.png","attachments":[{"devicePath":"/workspace/circles/member/inbox/mattermost/thread-1/post-1/mascot.png","filename":"mascot.png","contentType":"image/png","sizeBytes":5,"contentBase64":"aW1hZ2U="}]}}`} toolCatalogBuilder := newFileToolTestCatalogBuilder(workspacePath) toolCatalogBuilder.UseTestCapabilityToolDescriptors(capability.Client{Endpoint: "http://capability.local", HTTPClient: httpClient}, []CapabilityToolDescriptor{canonicalReadDescriptor("image_read")}) toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ ToolName: "image_read", - Input: toolcontract.MarshalToolInput(map[string]string{"path": "/workspace/circles/staff/inbox/mattermost/thread-1/post-1/mascot.png"}), + Input: toolcontract.MarshalToolInput(map[string]string{"path": "/workspace/circles/member/inbox/mattermost/thread-1/post-1/mascot.png"}), }) if errorValue != nil { @@ -299,7 +299,7 @@ func TestImageReadUsesExactPathInput(t *testing.T) { if result.Failed() { t.Fatalf("expected image_read success, got %s", result.ContentText()) } - if !strings.Contains(httpClient.requestBody, `/workspace/circles/staff/inbox/mattermost/thread-1/post-1/mascot.png`) { + if !strings.Contains(httpClient.requestBody, `/workspace/circles/member/inbox/mattermost/thread-1/post-1/mascot.png`) { t.Fatalf("expected capability request to use exact path, got %s", httpClient.requestBody) } } @@ -314,7 +314,7 @@ func TestCanonicalReadRejectsMaterialIDInput(t *testing.T) { ProfileName: "default", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -571,7 +571,7 @@ func TestImageGenerateSendsRequesterWorkspacePathToBridge(t *testing.T) { RequesterPersonID: "person-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) diff --git a/internal/agentruntime/conversation_scope.go b/internal/agentruntime/conversation_scope.go index 12447d3c..f5c0b167 100644 --- a/internal/agentruntime/conversation_scope.go +++ b/internal/agentruntime/conversation_scope.go @@ -3,6 +3,8 @@ package agentruntime import ( "path/filepath" "strings" + + "github.com/yeomyeonggeori/blueclaw/internal/policy" ) type ConversationResourceScope struct { @@ -37,7 +39,7 @@ func ConversationScopeForRequest(workspaceRootPath string, request ToolCatalogRe } circleID := circleIDFromConversation(request) if circleID == "" { - circleID = "staff" + circleID = policy.MemberCircleID } return ConversationResourceScope{ Kind: "circle", diff --git a/internal/agentruntime/file_hint_resolver_test.go b/internal/agentruntime/file_hint_resolver_test.go index eab5ecb0..10bb8fd5 100644 --- a/internal/agentruntime/file_hint_resolver_test.go +++ b/internal/agentruntime/file_hint_resolver_test.go @@ -22,7 +22,7 @@ func TestFileReadResolvesAttachmentFileHint(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, VisibleContext: agentcontract.VisibleContext{CurrentMaterials: []agentcontract.VisibleContextMaterial{{ FileHint: "attachment:mattermost:file-1", MaterialID: "mattermost:file-1", @@ -53,7 +53,7 @@ func TestFilePreviewResolvesArtifactFileHint(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, VisibleContext: agentcontract.VisibleContext{Materials: []agentcontract.VisibleContextMaterial{{ FileHint: fileHint, Path: filepath.Join(workspacePath, filepath.FromSlash(relativePath)), @@ -82,7 +82,7 @@ func TestFileHintRejectsUnknownAndForgedValues(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) for _, fileHint := range []string{ "attachment:mattermost:forged", @@ -113,7 +113,7 @@ func TestFileToolsPreserveExplicitPathResolutionAndAccess(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) readResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ diff --git a/internal/agentruntime/file_tools.go b/internal/agentruntime/file_tools.go index aeef3a02..eeb14093 100644 --- a/internal/agentruntime/file_tools.go +++ b/internal/agentruntime/file_tools.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "errors" + "github.com/yeomyeonggeori/blueclaw/internal/policy" "github.com/yeomyeonggeori/bluecollar/toolcontract" "mime" "path/filepath" @@ -361,7 +362,7 @@ func (toolCatalogBuilder *ToolCatalogBuilder) isOptionalControlFilePath(path str func (toolCatalogBuilder *ToolCatalogBuilder) recommendedSiteControlWritePath(path string) string { cleanPath := strings.Trim(filepath.ToSlash(strings.TrimSpace(path)), "/") - for _, prefix := range []string{"~/sites/", "home/sites/", "workspace/circles/staff/sites/"} { + for _, prefix := range []string{"~/sites/", "home/sites/", "workspace/circles/member/sites/"} { if recommendedPath := toolCatalogBuilder.recommendedSiteControlWritePathForPrefix(cleanPath, prefix); recommendedPath != "" { return recommendedPath } @@ -384,7 +385,7 @@ func (toolCatalogBuilder *ToolCatalogBuilder) recommendedSiteControlWritePathFor if !toolCatalogBuilder.isOptionalControlFilePath(relativePath) { return "" } - return filepath.ToSlash(filepath.Join("/workspace", "circles", "staff", "sites", siteID, "draft", relativePath)) + return filepath.ToSlash(filepath.Join("/workspace", "circles", policy.MemberCircleID, "sites", siteID, "draft", relativePath)) } func fileReadResultMap(base map[string]any, readResult fileReadOutput) map[string]any { diff --git a/internal/agentruntime/file_tools_test.go b/internal/agentruntime/file_tools_test.go index dfef66f8..9b9c0d28 100644 --- a/internal/agentruntime/file_tools_test.go +++ b/internal/agentruntime/file_tools_test.go @@ -55,7 +55,7 @@ func TestFileAttachToolAttachesSinglePath(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -91,7 +91,7 @@ func TestFileAttachToolAttachesMultipleFiles(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -131,7 +131,7 @@ func TestFileToolsAcceptVirtualHomePathsWithoutLeakingHostPath(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) writeResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -184,7 +184,7 @@ func TestFileDeliverAcceptsVirtualHomePathReturnedByFileRead(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) readResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -229,7 +229,7 @@ func TestFileReadResolvesSiteRelativePathNativelyAndFailsAsNotFound(t *testing.T toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -253,7 +253,7 @@ func TestFileReadTreatsMissingSiteControlFileAsOptionalState(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -270,7 +270,7 @@ func TestFileReadTreatsMissingSiteControlFileAsOptionalState(t *testing.T) { } if !strings.Contains(result.ContentText(), `"exists":false`) || !strings.Contains(result.ContentText(), `"optional":true`) || - !strings.Contains(result.ContentText(), `"recommendedWritePath":"/workspace/circles/staff/sites/site-1/draft/.internkim/artifact-brief.md"`) { + !strings.Contains(result.ContentText(), `"recommendedWritePath":"/workspace/circles/member/sites/site-1/draft/.internkim/artifact-brief.md"`) { t.Fatalf("expected optional missing control-file payload, got %s", result.ContentText()) } @@ -294,7 +294,7 @@ func TestFileWriteAcceptsPortablePathAndContent(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) writeResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -401,7 +401,7 @@ func TestFileReadReturnsLineRangeMetadata(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) readResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -443,7 +443,7 @@ func TestFileReadReturnsRangeAfterOldPrefixLimit(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) readResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -473,7 +473,7 @@ func TestFilePreviewReturnsTextPreview(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) previewResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -499,7 +499,7 @@ func TestFilePreviewUsesCachedAttachmentPreview(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, InputParts: []agentcontract.AgentPart{{ Type: agentcontract.AgentPartTypeFile, File: &agentcontract.AgentFilePart{ @@ -534,7 +534,7 @@ func TestFilePreviewUsesCachedAttachmentPreviewByMaterialID(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, InputParts: []agentcontract.AgentPart{{ Type: agentcontract.AgentPartTypeFile, File: &agentcontract.AgentFilePart{ @@ -575,7 +575,7 @@ func TestFileReadUsesCachedAttachmentPreviewWhenMaterialFileIsNotMounted(t *test toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, InputParts: []agentcontract.AgentPart{{ Type: agentcontract.AgentPartTypeFile, File: &agentcontract.AgentFilePart{ @@ -621,7 +621,7 @@ func TestFilePreviewResolvesAttachmentMaterialID(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, AttachmentMaterialResolver: staticAttachmentMaterialResolver{ material: agentcontract.VisibleContextMaterial{ MaterialID: "mattermost:file-1", @@ -656,7 +656,7 @@ func TestFilePreviewFallsBackFromStaleAttachmentPathToMaterialID(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, VisibleContext: agentcontract.VisibleContext{ CurrentMaterials: []agentcontract.VisibleContextMaterial{{ MaterialID: "mattermost:file-1", @@ -699,7 +699,7 @@ func TestFileReadFallsBackFromStaleAttachmentPathToMaterialID(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, VisibleContext: agentcontract.VisibleContext{ CurrentMaterials: []agentcontract.VisibleContextMaterial{{ MaterialID: "mattermost:file-1", @@ -740,7 +740,7 @@ func TestFileReadRejectsImageAttachmentMaterialFallback(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, VisibleContext: agentcontract.VisibleContext{ CurrentMaterials: []agentcontract.VisibleContextMaterial{{ MaterialID: "mattermost:file-1", @@ -778,7 +778,7 @@ func TestFilePreviewUsesResolvedAttachmentPreviewWithoutWorkspaceStat(t *testing toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, AttachmentMaterialResolver: staticAttachmentMaterialResolver{ material: agentcontract.VisibleContextMaterial{ MaterialID: "mattermost:file-1", @@ -816,7 +816,7 @@ func TestFileEditReplacesSingleExactMatch(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) editResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -853,7 +853,7 @@ func TestFileEditRejectsAmbiguousExactMatch(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) editResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -892,7 +892,7 @@ func TestFilePatchAppliesMultipleExactEdits(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) patchResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -924,7 +924,7 @@ func TestFilePatchValidationIsAllOrNothing(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) patchResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -973,7 +973,7 @@ func TestFileWriteFailsWithAccessDeniedWhenPOSIXDeniesCircleDirectory(t *testing RequesterPersonID: "person-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1006,7 +1006,7 @@ func TestFileDeliverFailsWithAccessDeniedWhenPOSIXDeniesCircleDirectory(t *testi RequesterPersonID: "person-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1038,7 +1038,7 @@ func TestFileReadFailsWithAccessDeniedWhenPOSIXDeniesCircleDirectory(t *testing. RequesterPersonID: "person-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1069,7 +1069,7 @@ func TestFileReadAllowsCirclePathWhenPOSIXAllows(t *testing.T) { RequesterPersonID: "person-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff", "finance"}, + Circles: []string{"member", "finance"}, }, }) @@ -1095,7 +1095,7 @@ func TestFileWriteAllowsCirclePathWhenPOSIXAllows(t *testing.T) { RequesterPersonID: "person-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff", "finance"}, + Circles: []string{"member", "finance"}, }, }) @@ -1124,7 +1124,7 @@ func TestFileWriteDefaultsToPrivateScopeForDirectMessage(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1162,7 +1162,7 @@ func TestFileWriteDefaultsToCircleScopeForCircleChannel(t *testing.T) { ConversationChannelName: "circle-finance", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff", "finance"}, + Circles: []string{"member", "finance"}, }, }) @@ -1185,7 +1185,7 @@ func TestFileWriteDefaultsToCircleScopeForCircleChannel(t *testing.T) { } } -func TestFileWriteDefaultsToStaffScopeForGeneralChannel(t *testing.T) { +func TestFileWriteDefaultsToMemberScopeForGeneralChannel(t *testing.T) { workspacePath := t.TempDir() toolCatalogBuilder := newFileToolTestCatalogBuilder(workspacePath) toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ @@ -1197,7 +1197,7 @@ func TestFileWriteDefaultsToStaffScopeForGeneralChannel(t *testing.T) { ConversationChannelName: "town-square", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1205,18 +1205,18 @@ func TestFileWriteDefaultsToStaffScopeForGeneralChannel(t *testing.T) { ToolName: "file_write", Input: toolcontract.MarshalToolInput(map[string]string{ "path": "status.md", - "content": "staff", + "content": "member", }), }) if errorValue != nil { t.Fatal(errorValue) } if result.Failed() { - t.Fatalf("expected staff write success, got %+v", result) + t.Fatalf("expected member write success, got %+v", result) } expectedPath := filepath.Join(workspacePath, "private", "people", "person-1", "status.md") - if document, errorValue := os.ReadFile(expectedPath); errorValue != nil || string(document) != "staff" { - t.Fatalf("expected staff file at %s, got %q and %v", expectedPath, string(document), errorValue) + if document, errorValue := os.ReadFile(expectedPath); errorValue != nil || string(document) != "member" { + t.Fatalf("expected member file at %s, got %q and %v", expectedPath, string(document), errorValue) } } @@ -1234,7 +1234,7 @@ func TestFileAttachDefaultsToPrivateScopeForDirectMessage(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1267,7 +1267,7 @@ func TestFileDeliverPersistsDocumentToDocuments(t *testing.T) { ProfileName: "default", RequesterPersonID: "person-1", ConversationID: "dm:channel-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -1295,7 +1295,7 @@ func TestFileDeliverCanDeliverDraftOutput(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1342,7 +1342,7 @@ func TestFileDeliverResolvesSamePathSpellingsAsTerminalWrite(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1390,7 +1390,7 @@ func TestFileDeliverNotFoundIncludesCandidateFiles(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1442,7 +1442,7 @@ func TestFileWriteAllowsManagedSitePackageManifest(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) managedResult, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -1485,7 +1485,7 @@ func TestFileWriteThroughWorkspaceActorTreatsContentAsData(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1536,7 +1536,7 @@ func TestFileWriteRespectsRequesterUmaskLikeTerminalRun(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1580,7 +1580,7 @@ func TestFileWriteAndTerminalRunShareRequesterWorkspaceActorView(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -1627,7 +1627,7 @@ func TestFileWriteRejectsLegacyMode(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) toolContext := toolcontract.WithTaskRunID(context.Background(), "run-mode-regression") diff --git a/internal/agentruntime/mcp_tool_provider_test.go b/internal/agentruntime/mcp_tool_provider_test.go index 8c1978d8..8cbd37bc 100644 --- a/internal/agentruntime/mcp_tool_provider_test.go +++ b/internal/agentruntime/mcp_tool_provider_test.go @@ -107,7 +107,7 @@ func TestMCPToolProviderRejectsPolicyDeniedInvocation(t *testing.T) { request: ToolCatalogRequest{ PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, ResourceAccessRules: []policy.ResourceAccessPolicy{{ Resource: "tool:workspace_echo", Actions: []string{"execute"}, diff --git a/internal/agentruntime/memory_tools_test.go b/internal/agentruntime/memory_tools_test.go index f80fddfa..3327d294 100644 --- a/internal/agentruntime/memory_tools_test.go +++ b/internal/agentruntime/memory_tools_test.go @@ -146,9 +146,9 @@ func TestMemoryRememberToolRejectsInaccessibleActiveCircle(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{Circles: []string{"member"}}, ActiveCircleID: "admin", - MemoryNamespaces: []memory.MemoryNamespace{memory.CircleNamespace("default", "staff")}, + MemoryNamespaces: []memory.MemoryNamespace{memory.CircleNamespace("default", "member")}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -175,7 +175,7 @@ func TestMemoryRememberToolEnqueuesCircleMemoryForActiveCircle(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{Circles: []string{"staff", "hr-compensation"}}, + PersonAccess: policy.PersonAccess{Circles: []string{"member", "hr-compensation"}}, ActiveCircleID: "hr-compensation", MemoryNamespaces: []memory.MemoryNamespace{memory.CircleNamespace("default", "hr-compensation")}, }) @@ -209,7 +209,7 @@ func TestMemoryRememberToolRejectsMultipleActiveCircleCandidates(t *testing.T) { RequesterPersonID: "person-1", Prompt: "@admin @hr-compensation remember this", ConversationChannelName: "town-square", - PersonAccess: policy.PersonAccess{Circles: []string{"staff", "admin", "hr-compensation"}}, + PersonAccess: policy.PersonAccess{Circles: []string{"member", "admin", "hr-compensation"}}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -256,7 +256,7 @@ func TestMemorySearchUsesPersonAndActiveCircleNamespaces(t *testing.T) { RequesterPersonID: "person-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff", "hr-compensation", "admin"}, + Circles: []string{"member", "hr-compensation", "admin"}, }, ActiveCircleID: "hr-compensation", MemoryNamespaces: []memory.MemoryNamespace{ @@ -398,7 +398,7 @@ func TestMemorySearchReturnsRecoverableToolErrorWhenGraphitiFails(t *testing.T) toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, MemoryNamespaces: []memory.MemoryNamespace{memory.UserNamespace("person-1")}, }) diff --git a/internal/agentruntime/shell_tools_test.go b/internal/agentruntime/shell_tools_test.go index 3e7fe465..f12d1484 100644 --- a/internal/agentruntime/shell_tools_test.go +++ b/internal/agentruntime/shell_tools_test.go @@ -20,7 +20,7 @@ func TestTerminalRunTranslatesAgentWorkspacePaths(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -74,7 +74,7 @@ func TestTerminalRunRejectsInvalidInputShapes(t *testing.T) { toolRegistry := newTerminalToolTestCatalogBuilder(t.TempDir()).BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) testCases := []struct { name string @@ -113,7 +113,7 @@ func TestTerminalRunAcceptsUnusedApprovalReason(t *testing.T) { toolRegistry := newTerminalToolTestCatalogBuilder(t.TempDir()).BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result := invokeTerminalRunTestTool(t, toolRegistry, json.RawMessage(`{"command":"true","approvalRequired":false,"approvalReason":"No approval needed."}`)) @@ -128,7 +128,7 @@ func TestTerminalRunAcceptsApprovalFieldsAfterTheApprovalGate(t *testing.T) { toolRegistry := newTerminalToolTestCatalogBuilder(t.TempDir()).BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result := invokeTerminalRunTestTool(t, toolRegistry, json.RawMessage(`{"command":"true","approvalRequired":true,"approvalReason":"Publish the release."}`)) @@ -143,7 +143,7 @@ func TestTerminalRunFailureHasCanonicalData(t *testing.T) { toolRegistry := newTerminalToolTestCatalogBuilder(t.TempDir()).BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -191,7 +191,7 @@ func TestTerminalRunAllowsStderrRedirection(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -215,7 +215,7 @@ func TestTerminalRunAllowsSourceFileWrite(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -239,7 +239,7 @@ func TestTerminalRunAllowsServiceOwnedPathText(t *testing.T) { toolRegistry := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", RequesterPersonID: "person-1", - PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"staff"}}, + PersonAccess: policy.PersonAccess{PersonID: "person-1", Circles: []string{"member"}}, }) result, errorValue := toolRegistry.Invoke(context.Background(), toolcontract.ToolInvocation{ @@ -265,7 +265,7 @@ func TestTerminalRunDefaultsToPrivateScopeForDirectMessage(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -302,7 +302,7 @@ func TestTerminalRunMaterializesRequesterRuntimeEnvironment(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -356,7 +356,7 @@ func TestTerminalRunScopesTaskTemporaryDirectoryToTheTaskRun(t *testing.T) { ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -404,7 +404,7 @@ func TestTerminalRunRelativeWorkingDirectoryUsesConversationDefault(t *testing.T ConversationID: "dm:channel-1", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) @@ -467,7 +467,7 @@ func TestTerminalRunFailsWhenPOSIXDeniesCircleWorkingDirectory(t *testing.T) { ProfileName: "default", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, }, }) diff --git a/internal/agentruntime/task_launcher_test.go b/internal/agentruntime/task_launcher_test.go index d39ef258..2abeb452 100644 --- a/internal/agentruntime/task_launcher_test.go +++ b/internal/agentruntime/task_launcher_test.go @@ -264,7 +264,7 @@ func TestTaskLauncherRejectsStaleMessageToolRegistryBeforeModelCall(t *testing.T } } -func TestTaskLauncherAddsStaffToRequesterAccess(t *testing.T) { +func TestTaskLauncherAddsMemberToRequesterAccess(t *testing.T) { personAccess := requesterPersonAccessForTaskLaunch(TaskLaunchRequest{ RequesterPersonID: "person-1", PersonAccess: policy.PersonAccess{ @@ -275,8 +275,8 @@ func TestTaskLauncherAddsStaffToRequesterAccess(t *testing.T) { if personAccess.PersonID != "person-1" { t.Fatalf("expected requester person id to be copied, got %+v", personAccess) } - if !containsString(personAccess.Circles, "staff") || !containsString(personAccess.Circles, "finance") { - t.Fatalf("expected task requester access to include staff and explicit circles, got %+v", personAccess.Circles) + if !containsString(personAccess.Circles, "member") || !containsString(personAccess.Circles, "finance") { + t.Fatalf("expected task requester access to include the member circle and explicit circles, got %+v", personAccess.Circles) } } @@ -712,26 +712,26 @@ func TestCapabilityToolExecutionUsesResourceAccess(t *testing.T) { "default": {"company_broadcast_send"}, }, nil) - staffToolSet := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ + memberToolSet := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", PersonAccess: policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff"}, + Circles: []string{"member"}, ResourceAccessRules: resourceAccessRules, }, }) - staffResult, errorValue := staffToolSet.Invoke(context.Background(), toolcontract.ToolInvocation{ + memberResult, errorValue := memberToolSet.Invoke(context.Background(), toolcontract.ToolInvocation{ ToolName: "company_broadcast_send", Input: json.RawMessage(`{"message":"hello"}`), }) if errorValue != nil { t.Fatalf("expected denied tool result: %v", errorValue) } - if !staffResult.Failed() || !strings.Contains(staffResult.ContentText(), "tool is not allowed") { - t.Fatalf("expected staff execution denial, got %+v", staffResult) + if !memberResult.Failed() || !strings.Contains(memberResult.ContentText(), "tool is not allowed") { + t.Fatalf("expected member execution denial, got %+v", memberResult) } - if strings.Contains(staffToolSet.Descriptions(), "company_broadcast_send") { - t.Fatalf("expected denied tool to be omitted from catalog, got %s", staffToolSet.Descriptions()) + if strings.Contains(memberToolSet.Descriptions(), "company_broadcast_send") { + t.Fatalf("expected denied tool to be omitted from catalog, got %s", memberToolSet.Descriptions()) } if httpClient.requestPath != "" { t.Fatalf("expected denied tool not to call capability bridge, got path=%s", httpClient.requestPath) @@ -741,7 +741,7 @@ func TestCapabilityToolExecutionUsesResourceAccess(t *testing.T) { ProfileName: "default", PersonAccess: policy.PersonAccess{ PersonID: "person-2", - Circles: []string{"staff", "representative"}, + Circles: []string{"member", "representative"}, ResourceAccessRules: resourceAccessRules, }, }) @@ -760,11 +760,11 @@ func TestCapabilityToolExecutionUsesResourceAccess(t *testing.T) { } } -func TestFlowTaskAddToolRequiresStaffCircle(t *testing.T) { +func TestFlowTaskAddToolRequiresMemberCircle(t *testing.T) { resourceAccessRules := []policy.ResourceAccessPolicy{{ Resource: "tool:task_add", Actions: []string{"execute"}, - Circles: []string{"staff"}, + Circles: []string{"member"}, }} httpClient := &recordingHTTPClient{} toolCatalogBuilder := NewToolCatalogBuilder() @@ -794,23 +794,23 @@ func TestFlowTaskAddToolRequiresStaffCircle(t *testing.T) { t.Fatalf("expected denied Flow tool not to call capability bridge, got path=%s", httpClient.requestPath) } - staffToolSet := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ + memberToolSet := toolCatalogBuilder.BuildToolSet(ToolCatalogRequest{ ProfileName: "default", PersonAccess: policy.PersonAccess{ PersonID: "person-2", - Circles: []string{"staff"}, + Circles: []string{"member"}, ResourceAccessRules: resourceAccessRules, }, }) - staffResult, errorValue := staffToolSet.Invoke(context.Background(), toolcontract.ToolInvocation{ + memberResult, errorValue := memberToolSet.Invoke(context.Background(), toolcontract.ToolInvocation{ ToolName: "task_add", Input: json.RawMessage(`{"title":"10분 회의"}`), }) if errorValue != nil { - t.Fatalf("expected staff tool result: %v", errorValue) + t.Fatalf("expected the member circle tool result: %v", errorValue) } - if staffResult.Failed() { - t.Fatalf("expected staff execution success, got %+v", staffResult) + if memberResult.Failed() { + t.Fatalf("expected member execution success, got %+v", memberResult) } if httpClient.requestPath != "/v1/tools/task_add/invoke" { t.Fatalf("expected Flow capability bridge call, got path=%s body=%s", httpClient.requestPath, httpClient.requestBody) diff --git a/internal/connectors/runtime_test.go b/internal/connectors/runtime_test.go index 09ae5c25..5a07db24 100644 --- a/internal/connectors/runtime_test.go +++ b/internal/connectors/runtime_test.go @@ -2176,7 +2176,7 @@ func TestConnectorRuntimeAddsDocumentAttachmentCatalog(t *testing.T) { MessageID: "message-1", Filename: "report.pdf", ContentType: "application/pdf", - Path: "/workspace/circles/staff/inbox/mattermost/direct-1/message-1/report.pdf", + Path: "/workspace/circles/member/inbox/mattermost/direct-1/message-1/report.pdf", IsAvailable: true, }}, } @@ -2188,7 +2188,7 @@ func TestConnectorRuntimeAddsDocumentAttachmentCatalog(t *testing.T) { t.Fatalf("expected event to process: %v", errorValue) } body := joinConnectorMessageContent(languageModel.request.Messages) - for _, expected := range []string{"Current attachments", "materialID=mattermost:file-1", "path=/workspace/circles/staff/inbox/mattermost/direct-1/message-1/report.pdf", "availableTools=file_preview,file_read"} { + for _, expected := range []string{"Current attachments", "materialID=mattermost:file-1", "path=/workspace/circles/member/inbox/mattermost/direct-1/message-1/report.pdf", "availableTools=file_preview,file_read"} { if !strings.Contains(body, expected) { t.Fatalf("expected attachment catalog %q in model request, got %s", expected, body) } @@ -2213,7 +2213,7 @@ func TestConnectorRuntimeAddsUnavailableAttachmentCatalog(t *testing.T) { MessageID: "message-1", Filename: "archive.bin", ContentType: "application/octet-stream", - Path: "/workspace/circles/staff/inbox/mattermost/direct-1/message-1/archive.bin", + Path: "/workspace/circles/member/inbox/mattermost/direct-1/message-1/archive.bin", IsAvailable: false, ErrorCode: "download_failed", Message: "unsupported format", @@ -2254,7 +2254,7 @@ func TestConnectorAttachmentMaterialResolverImportsHistoryMaterial(t *testing.T) Filename: "mascot.png", ContentType: "image/png", SizeBytes: 5, - Path: "/workspace/circles/staff/inbox/mattermost/thread-1/mascot.png", + Path: "/workspace/circles/member/inbox/mattermost/thread-1/mascot.png", IsAvailable: true, }}, }, @@ -2272,7 +2272,7 @@ func TestConnectorAttachmentMaterialResolverImportsHistoryMaterial(t *testing.T) if errorValue != nil { t.Fatalf("expected history material to resolve: %v", errorValue) } - if material.MaterialID != "mattermost:file-1" || material.Path != "/workspace/circles/staff/inbox/mattermost/thread-1/mascot.png" { + if material.MaterialID != "mattermost:file-1" || material.Path != "/workspace/circles/member/inbox/mattermost/thread-1/mascot.png" { t.Fatalf("expected imported history material, got %+v", material) } if len(adapter.historyCursors) != 1 || adapter.historyCursors[0] != "history-cursor" { diff --git a/internal/e2e/scenarios.go b/internal/e2e/scenarios.go index c693f26f..b30853ca 100644 --- a/internal/e2e/scenarios.go +++ b/internal/e2e/scenarios.go @@ -138,20 +138,20 @@ func PresentationLocalMultiturnSuccessScenario(artifactDirectoryPath string) Vir ExpectedAttachments: []string{".pptx", ".pdf", ".html", "-notes.txt"}, ExpectedWorkspaceFiles: []VirtualWorkspaceFileExpectation{ { - PathGlob: "circles/staff/tmp/*/DESIGN.md", + PathGlob: "circles/member/tmp/*/DESIGN.md", ContainsFragments: []string{"colors:", "Visual direction"}, }, { - PathGlob: "circles/staff/tmp/*/presentation.md", + PathGlob: "circles/member/tmp/*/presentation.md", ContainsFragments: []string{"design-source: DESIGN.md", "InternKim capability deck", "너 뭐 할 수 있는지"}, ForbiddenFragments: []string{"Draft a presentation deck", "user_request:"}, }, { - PathGlob: "circles/staff/tmp/*/review/slide-review.json", + PathGlob: "circles/member/tmp/*/review/slide-review.json", ContainsFragments: []string{`"passed": true`, `"safeMargin": true`, `"edgeOverflow": true`, `"contactSheets"`}, }, { - PathGlob: "circles/staff/tmp/*/*.html", + PathGlob: "circles/member/tmp/*/*.html", ContainsFragments: []string{"Paperlogy", "Freesentation", "--background", "InternKim capability deck"}, }, }, @@ -333,7 +333,7 @@ func AttachmentMaterialReadScenario(artifactDirectoryPath string) VirtualSession }}, ContextMaterials: []connectors.InputAttachment{attachment}, ActionResponses: []string{ - actionCallTool("image_read", `{"path":"/workspace/circles/staff/inbox/virtual/virtual-conversation-1/virtual-message-001/mascot.png"}`), + actionCallTool("image_read", `{"path":"/workspace/circles/member/inbox/virtual/virtual-conversation-1/virtual-message-001/mascot.png"}`), actionFinishMessage("이미지를 확인했습니다.", "obs-001:image_read:0"), }, ExpectedToolCalls: []string{"image_read"}, @@ -1181,8 +1181,8 @@ func SitePrototypeAcceptanceScenario(artifactDirectoryPath string) VirtualSessio Turns: []VirtualTurn{{ Prompt: "테스트용 'Local Fleet Studio' 단일 페이지 소개 웹사이트를 만들어서 배포해줘. 첫 화면 제목은 'Local Fleet Studio', 보조 문구는 '로컬 플릿 웹사이트 생성 배포 테스트', 섹션은 서비스 소개, 장점 3개, 문의 CTA만 넣어줘. 추가 질문하지 말고 합리적인 기본값으로 진행해줘.", ActionResponses: []string{ - actionCallTool("file_write", `{"path":"/workspace/circles/staff/sites/local-fleet-studio/draft/app/public/site-content.json","content":"{\"siteName\":\"Local Fleet Studio\",\"tagline\":\"로컬 플릿 웹사이트 생성 배포 테스트\",\"blocks\":[{\"variant\":\"hero\",\"title\":\"Local Fleet Studio\",\"body\":\"로컬 플릿 웹사이트 생성 배포 테스트\"},{\"variant\":\"prose\",\"title\":\"서비스 소개\",\"body\":\"Local Fleet Studio는 로컬 플릿 환경에서 웹사이트 생성과 배포 과정을 검증하는 테스트 서비스입니다.\"},{\"variant\":\"features\",\"title\":\"장점\",\"items\":[{\"title\":\"빠른 프로토타입\",\"body\":\"빠른 프로토타입 생성\"},{\"title\":\"안전한 검증\",\"body\":\"안전한 배포 검증\"},{\"title\":\"손쉬운 재배포\",\"body\":\"손쉬운 재배포\"}]},{\"variant\":\"cta\",\"title\":\"문의\",\"body\":\"자세한 내용이 궁금하시면 지금 바로 문의해 주세요.\"}]}"}`), - actionInvokeCapabilityTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/staff/sites/local-fleet-studio/draft","mode":"publish"}`), + actionCallTool("file_write", `{"path":"/workspace/circles/member/sites/local-fleet-studio/draft/app/public/site-content.json","content":"{\"siteName\":\"Local Fleet Studio\",\"tagline\":\"로컬 플릿 웹사이트 생성 배포 테스트\",\"blocks\":[{\"variant\":\"hero\",\"title\":\"Local Fleet Studio\",\"body\":\"로컬 플릿 웹사이트 생성 배포 테스트\"},{\"variant\":\"prose\",\"title\":\"서비스 소개\",\"body\":\"Local Fleet Studio는 로컬 플릿 환경에서 웹사이트 생성과 배포 과정을 검증하는 테스트 서비스입니다.\"},{\"variant\":\"features\",\"title\":\"장점\",\"items\":[{\"title\":\"빠른 프로토타입\",\"body\":\"빠른 프로토타입 생성\"},{\"title\":\"안전한 검증\",\"body\":\"안전한 배포 검증\"},{\"title\":\"손쉬운 재배포\",\"body\":\"손쉬운 재배포\"}]},{\"variant\":\"cta\",\"title\":\"문의\",\"body\":\"자세한 내용이 궁금하시면 지금 바로 문의해 주세요.\"}]}"}`), + actionInvokeCapabilityTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/member/sites/local-fleet-studio/draft","mode":"publish"}`), actionFinishMessage("Local Fleet Studio 웹사이트 프로토타입을 배포했습니다: https://local-fleet-studio.device.example.test", "obs-002:site_serve:0"), }, CompletionJudgeResponses: []string{completionJudgeSatisfiedResponse()}, @@ -1222,8 +1222,8 @@ func SiteEditRedeployAcceptanceScenario(artifactDirectoryPath string) VirtualSes { Prompt: "Build and deploy a single-page Local Fleet Studio website. Use the heading 'Local Fleet Studio' and subtitle 'Local fleet create deploy test'. Include a short service overview and three feature bullets. Do not ask follow-up questions.", ActionResponses: []string{ - actionCallTool("file_write", `{"path":"/workspace/circles/staff/sites/local-fleet-studio/draft/app/public/site-content.json","content":"{\"siteName\":\"Local Fleet Studio\",\"tagline\":\"Local fleet create deploy test\",\"blocks\":[{\"variant\":\"hero\",\"title\":\"Local Fleet Studio\",\"body\":\"Local fleet create deploy test\"},{\"variant\":\"prose\",\"title\":\"Overview\",\"body\":\"Local Fleet Studio validates local fleet website creation and deployment.\"},{\"variant\":\"features\",\"title\":\"Features\",\"items\":[{\"title\":\"Fast prototyping\",\"body\":\"Fast prototyping\"},{\"title\":\"Safe verification\",\"body\":\"Safe deploy verification\"},{\"title\":\"Easy redeploys\",\"body\":\"Easy redeploys\"}]}]}"}`), - actionInvokeCapabilityTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/staff/sites/local-fleet-studio/draft","mode":"publish"}`), + actionCallTool("file_write", `{"path":"/workspace/circles/member/sites/local-fleet-studio/draft/app/public/site-content.json","content":"{\"siteName\":\"Local Fleet Studio\",\"tagline\":\"Local fleet create deploy test\",\"blocks\":[{\"variant\":\"hero\",\"title\":\"Local Fleet Studio\",\"body\":\"Local fleet create deploy test\"},{\"variant\":\"prose\",\"title\":\"Overview\",\"body\":\"Local Fleet Studio validates local fleet website creation and deployment.\"},{\"variant\":\"features\",\"title\":\"Features\",\"items\":[{\"title\":\"Fast prototyping\",\"body\":\"Fast prototyping\"},{\"title\":\"Safe verification\",\"body\":\"Safe deploy verification\"},{\"title\":\"Easy redeploys\",\"body\":\"Easy redeploys\"}]}]}"}`), + actionInvokeCapabilityTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/member/sites/local-fleet-studio/draft","mode":"publish"}`), actionFinishMessage("Deployed the Local Fleet Studio site: https://local-fleet-studio.device.example.test", "obs-002:site_serve:0"), }, CompletionJudgeResponses: []string{completionJudgeSatisfiedResponse()}, @@ -1240,8 +1240,8 @@ func SiteEditRedeployAcceptanceScenario(artifactDirectoryPath string) VirtualSes Prompt: "Update the same Local Fleet Studio website heading to say 'Local Fleet Studio Updated' and add the subtitle 'Redeploy verification passed', then redeploy the same site. Do not create a new site.", ActionResponses: []string{ actionCallTool("site_list", `{}`), - actionCallTool("file_write", `{"path":"/workspace/circles/staff/sites/local-fleet-studio/draft/app/public/site-content.json","content":"{\"siteName\":\"Local Fleet Studio Updated\",\"tagline\":\"Redeploy verification passed\",\"blocks\":[{\"variant\":\"hero\",\"title\":\"Local Fleet Studio Updated\",\"body\":\"Redeploy verification passed\"}]}"}`), - actionInvokeCapabilityTool("site_serve", `{"title":"Local Fleet Studio Updated","sourceWorkspacePath":"/workspace/circles/staff/sites/local-fleet-studio/draft","mode":"publish","siteReference":"local-fleet-studio"}`), + actionCallTool("file_write", `{"path":"/workspace/circles/member/sites/local-fleet-studio/draft/app/public/site-content.json","content":"{\"siteName\":\"Local Fleet Studio Updated\",\"tagline\":\"Redeploy verification passed\",\"blocks\":[{\"variant\":\"hero\",\"title\":\"Local Fleet Studio Updated\",\"body\":\"Redeploy verification passed\"}]}"}`), + actionInvokeCapabilityTool("site_serve", `{"title":"Local Fleet Studio Updated","sourceWorkspacePath":"/workspace/circles/member/sites/local-fleet-studio/draft","mode":"publish","siteReference":"local-fleet-studio"}`), actionFinishMessage("Updated and redeployed the site: https://local-fleet-studio.device.example.test", "obs-002:file_write:0", "obs-003:site_serve:0"), }, CompletionJudgeResponses: []string{completionJudgeSatisfiedResponse()}, @@ -1279,10 +1279,10 @@ func SiteCustomStructureAcceptanceScenario(artifactDirectoryPath string) Virtual Turns: []VirtualTurn{{ Prompt: "Local Fleet Studio 웹사이트 레이아웃을 두 칼럼 커스텀 구조로 바꿔서 다시 배포해줘.", ActionResponses: []string{ - actionCallTool("file_write", `{"path":"/workspace/circles/staff/sites/demo/draft/app/src/App.tsx","content":"export default function App() {\n return
Local Fleet Studio
Two-column custom layout
;\n}\n"}`), - actionCallTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/staff/sites/demo/draft","mode":"publish","siteReference":"demo"}`), - actionCallTool("shell", `{"command":"mkdir -p dist && printf '
Local Fleet Studio
Two-column custom layout
' > dist/index.html","workingDirectoryPath":"/workspace/circles/staff/sites/demo/draft/app","timeoutSecond":120}`), - actionCallTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/staff/sites/demo/draft","mode":"publish","siteReference":"demo"}`), + actionCallTool("file_write", `{"path":"/workspace/circles/member/sites/demo/draft/app/src/App.tsx","content":"export default function App() {\n return
Local Fleet Studio
Two-column custom layout
;\n}\n"}`), + actionCallTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/member/sites/demo/draft","mode":"publish","siteReference":"demo"}`), + actionCallTool("shell", `{"command":"mkdir -p dist && printf '
Local Fleet Studio
Two-column custom layout
' > dist/index.html","workingDirectoryPath":"/workspace/circles/member/sites/demo/draft/app","timeoutSecond":120}`), + actionCallTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/member/sites/demo/draft","mode":"publish","siteReference":"demo"}`), actionFinishMessage("커스텀 레이아웃을 빌드하고 다시 배포했습니다: https://demo.device.example.test", "obs-005:site_serve:0"), }, CompletionJudgeResponses: []string{completionJudgeSatisfiedResponse()}, @@ -1323,8 +1323,8 @@ func SiteLifecycleAcceptanceScenario(artifactDirectoryPath string) VirtualSessio "site_serve", }, ActionResponses: []string{ - actionCallTool("shell", `{"command":"mkdir -p dist && printf '

Local Fleet Studio

로컬 플릿 웹사이트 CRUD 테스트

' > dist/index.html","workingDirectoryPath":"/workspace/circles/staff/sites/local-fleet-studio/draft/app","timeoutSecond":120}`), - actionInvokeCapabilityTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/staff/sites/local-fleet-studio/draft","mode":"publish"}`), + actionCallTool("shell", `{"command":"mkdir -p dist && printf '

Local Fleet Studio

로컬 플릿 웹사이트 CRUD 테스트

' > dist/index.html","workingDirectoryPath":"/workspace/circles/member/sites/local-fleet-studio/draft/app","timeoutSecond":120}`), + actionInvokeCapabilityTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/member/sites/local-fleet-studio/draft","mode":"publish"}`), actionFinishMessage("Local Fleet Studio 웹사이트를 배포했습니다: https://local-fleet-studio.device.example.test", "obs-002:site_serve:0"), }, CompletionJudgeResponses: []string{completionJudgeSatisfiedResponse()}, @@ -1346,9 +1346,9 @@ func SiteLifecycleAcceptanceScenario(artifactDirectoryPath string) VirtualSessio }, ActionResponses: []string{ actionCallTool("site_list", `{}`), - actionCallTool("file_write", `{"path":"/workspace/circles/staff/sites/local-fleet-studio/draft/app/src/App.tsx","content":"export default function App() {\n return

Local Fleet Studio Updated

재배포 검증 완료

;\n}\n"}`), - actionCallTool("shell", `{"command":"mkdir -p dist && printf '

Local Fleet Studio Updated

재배포 검증 완료

' > dist/index.html","workingDirectoryPath":"/workspace/circles/staff/sites/local-fleet-studio/draft/app","timeoutSecond":120}`), - actionInvokeCapabilityTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/staff/sites/local-fleet-studio/draft","mode":"publish","siteReference":"local-fleet-studio"}`), + actionCallTool("file_write", `{"path":"/workspace/circles/member/sites/local-fleet-studio/draft/app/src/App.tsx","content":"export default function App() {\n return

Local Fleet Studio Updated

재배포 검증 완료

;\n}\n"}`), + actionCallTool("shell", `{"command":"mkdir -p dist && printf '

Local Fleet Studio Updated

재배포 검증 완료

' > dist/index.html","workingDirectoryPath":"/workspace/circles/member/sites/local-fleet-studio/draft/app","timeoutSecond":120}`), + actionInvokeCapabilityTool("site_serve", `{"title":"Local Fleet Studio","sourceWorkspacePath":"/workspace/circles/member/sites/local-fleet-studio/draft","mode":"publish","siteReference":"local-fleet-studio"}`), actionFinishMessage("Local Fleet Studio 웹사이트를 수정하고 다시 배포했습니다: https://local-fleet-studio.device.example.test", "obs-002:file_write:0", "obs-004:site_serve:0"), }, CompletionJudgeResponses: []string{completionJudgeSatisfiedResponse()}, diff --git a/internal/e2e/virtual_session.go b/internal/e2e/virtual_session.go index c6653779..34f60005 100644 --- a/internal/e2e/virtual_session.go +++ b/internal/e2e/virtual_session.go @@ -3712,14 +3712,14 @@ func testPolicyProjection() policy.PolicyProjection { PersonID: "person-1", DisplayName: "샘플", Emails: []string{"sample@example.com"}, - Circles: []string{"staff"}, + Circles: []string{policy.MemberCircleID}, SecurityLevelRank: 0, GrantedClasses: []string{}, }}, Circles: []policy.CirclePolicy{{ - CircleID: "staff", + CircleID: policy.MemberCircleID, DisplayName: "Staff", - WorkspaceDirectoryPath: "/workspace/circles/staff", + WorkspaceDirectoryPath: "/workspace/circles/member", }}, Channels: []policy.ChannelPolicy{{ Platform: "virtual", diff --git a/internal/e2e/virtual_session_test.go b/internal/e2e/virtual_session_test.go index 13084dce..40ae8ff2 100644 --- a/internal/e2e/virtual_session_test.go +++ b/internal/e2e/virtual_session_test.go @@ -1430,7 +1430,7 @@ func TestAttachmentMaterialRead(t *testing.T) { t.Fatalf("expected attachment material read scenario to pass: %v", errorValue) } turnResult := result.TurnResults[0] - if !eventsContain(turnResult.Events, "tool.image_read.requested", `"path":"/workspace/circles/staff/inbox/virtual/virtual-conversation-1/virtual-message-001/mascot.png"`) { + if !eventsContain(turnResult.Events, "tool.image_read.requested", `"path":"/workspace/circles/member/inbox/virtual/virtual-conversation-1/virtual-message-001/mascot.png"`) { t.Fatalf("expected image_read to use the exact workspace path; events: %s", summarizeEvents(turnResult.Events)) } if eventsContain(turnResult.Events, "tool.shell.requested", "shell") { diff --git a/internal/enrollment/materialize.go b/internal/enrollment/materialize.go index 7a9588ab..9e89341b 100644 --- a/internal/enrollment/materialize.go +++ b/internal/enrollment/materialize.go @@ -6,6 +6,8 @@ import ( "path/filepath" "strings" + "github.com/yeomyeonggeori/blueclaw/internal/policy" + "github.com/yeomyeonggeori/blueclaw/internal/config" ) @@ -64,13 +66,13 @@ func policyDocumentFor(enrollment Enrollment) map[string]any { "emails": []string{enrollment.Operator.Email}, "securityLevelName": "admin", "securityLevelRank": 100, - "circles": []string{"staff"}, + "circles": []string{policy.MemberCircleID}, "isAdmin": true, }}, "circles": []map[string]any{{ - "circleID": "staff", + "circleID": policy.MemberCircleID, "displayName": "Staff", - "workspaceDirectoryPath": filepath.Join(enrollment.WorkspaceRootPath, "circles", "staff"), + "workspaceDirectoryPath": filepath.Join(enrollment.WorkspaceRootPath, "circles", policy.MemberCircleID), }}, } } diff --git a/internal/httpserver/workspace_files_handler_test.go b/internal/httpserver/workspace_files_handler_test.go index d7307508..55437d85 100644 --- a/internal/httpserver/workspace_files_handler_test.go +++ b/internal/httpserver/workspace_files_handler_test.go @@ -71,7 +71,7 @@ func (actor stubWorkspaceActor) Stat(_ context.Context, path string) (security.W type stubPersonAccessResolver struct{} func (stubPersonAccessResolver) ResolvePersonAccess(personID string) policy.PersonAccess { - return policy.PersonAccess{PersonID: personID, Circles: []string{"staff"}} + return policy.PersonAccess{PersonID: personID, Circles: []string{"member"}} } func newWorkspaceFilesTestHandler(factory *stubWorkspaceActorFactory, workspaceRootPath string) WorkspaceFilesHandler { @@ -143,7 +143,7 @@ func TestWorkspaceFilesHandlerListsAPrivateHomeAsItsOwner(t *testing.T) { func TestWorkspaceFilesHandlerListsCircleAndPublicPathsAsTheSamePerson(t *testing.T) { factory := &stubWorkspaceActorFactory{entries: []security.WorkspaceActorDirectoryEntry{{Name: "spec.md", SizeBytes: 3}}} handler := newWorkspaceFilesTestHandler(factory, t.TempDir()) - for _, path := range []string{"/workspace/circles/staff", "/workspace/shared/public"} { + for _, path := range []string{"/workspace/circles/member", "/workspace/shared/public"} { recorder := httptest.NewRecorder() handler.HandleList(recorder, httptest.NewRequest(http.MethodGet, "/admin/api/workspace/list?personID=person-1&path="+path, nil)) if recorder.Code != http.StatusOK { diff --git a/internal/identity/identity_service_test.go b/internal/identity/identity_service_test.go index 8a2cb287..2db43b0f 100644 --- a/internal/identity/identity_service_test.go +++ b/internal/identity/identity_service_test.go @@ -85,7 +85,7 @@ func TestIdentityServiceSkipsStalePlatformAccountWithoutPolicyEmail(t *testing.T } } -func TestIdentityServiceResolvesRequesterAccessWithStaff(t *testing.T) { +func TestIdentityServiceResolvesRequesterAccessWithMember(t *testing.T) { identityService := NewIdentityService(policy.PolicyProjection{ PersonAccessByPersonID: map[string]policy.PersonAccess{ "person-1": {PersonID: "person-1", Circles: []string{"finance"}}, @@ -94,8 +94,8 @@ func TestIdentityServiceResolvesRequesterAccessWithStaff(t *testing.T) { personAccess := identityService.ResolvePersonAccess("person-1") - if !hasIdentityTestString(personAccess.Circles, "staff") || !hasIdentityTestString(personAccess.Circles, "finance") { - t.Fatalf("expected requester access to include staff and explicit circles, got %+v", personAccess.Circles) + if !hasIdentityTestString(personAccess.Circles, "member") || !hasIdentityTestString(personAccess.Circles, "finance") { + t.Fatalf("expected requester access to include the member circle and explicit circles, got %+v", personAccess.Circles) } } diff --git a/internal/memory/markdown_store_test.go b/internal/memory/markdown_store_test.go index dbe87c7d..a5401e70 100644 --- a/internal/memory/markdown_store_test.go +++ b/internal/memory/markdown_store_test.go @@ -141,8 +141,8 @@ func TestMemoryUpdateProcessorSkipsMarkdownForCircleMemory(t *testing.T) { processor := NewMemoryUpdateProcessor(memoryService, markdownStore) result := processor.Process(context.Background(), MemoryUpdateJob{ - Namespace: CircleNamespace("default", "staff"), - Content: "Staff prefers concise updates.", + Namespace: CircleNamespace("default", "member"), + Content: "Member prefers concise updates.", SenderPersonID: "person-1", }) diff --git a/internal/memory/memory_service_test.go b/internal/memory/memory_service_test.go index fa167a93..ad1a15ce 100644 --- a/internal/memory/memory_service_test.go +++ b/internal/memory/memory_service_test.go @@ -172,7 +172,7 @@ func TestMemoryServiceFiltersPrivateAndCircleResources(t *testing.T) { ownerFacts, errorValue := memoryService.SearchMemory(context.Background(), MemorySearchRequest{ ReaderPersonID: "person-1", - ReaderCircles: []string{"staff", "finance"}, + ReaderCircles: []string{"member", "finance"}, Namespaces: []MemoryNamespace{privateNamespace, financeNamespace}, }) if errorValue != nil { @@ -184,7 +184,7 @@ func TestMemoryServiceFiltersPrivateAndCircleResources(t *testing.T) { otherFacts, errorValue := memoryService.SearchMemory(context.Background(), MemorySearchRequest{ ReaderPersonID: "person-2", - ReaderCircles: []string{"staff"}, + ReaderCircles: []string{"member"}, Namespaces: []MemoryNamespace{privateNamespace, financeNamespace}, }) if errorValue != nil { @@ -222,7 +222,7 @@ func TestMemoryServiceAppliesResourceAccessRulesBeforeRanking(t *testing.T) { memoryFacts, errorValue := memoryService.SearchMemory(context.Background(), MemorySearchRequest{ ReaderPersonID: "person-1", - ReaderCircles: []string{"staff"}, + ReaderCircles: []string{"member"}, ResourceAccessRules: resourceAccessRules, Namespaces: []MemoryNamespace{ WorkspaceNamespace("default", 0, nil), diff --git a/internal/policy/policy_projection_service.go b/internal/policy/policy_projection_service.go index 83489b46..82475676 100644 --- a/internal/policy/policy_projection_service.go +++ b/internal/policy/policy_projection_service.go @@ -3,8 +3,8 @@ package policy import "strings" const ( - StaffCircleID = "staff" - AdminCircleID = "admin" + MemberCircleID = "member" + AdminCircleID = "admin" ) type PolicyProjection struct { @@ -61,7 +61,7 @@ func (policyProjectionService PolicyProjectionService) ReplacePolicyProjectionTr } func effectivePersonCircles(personPolicy PersonPolicy) []string { - circles := append([]string{StaffCircleID}, personPolicy.Circles...) + circles := append([]string{MemberCircleID}, personPolicy.Circles...) if personPolicy.IsAdmin { circles = append(circles, AdminCircleID) } @@ -69,7 +69,7 @@ func effectivePersonCircles(personPolicy PersonPolicy) []string { } func EnsureRequesterDefaults(personAccess PersonAccess) PersonAccess { - personAccess.Circles = normalizePolicyStrings(append([]string{StaffCircleID}, personAccess.Circles...)) + personAccess.Circles = normalizePolicyStrings(append([]string{MemberCircleID}, personAccess.Circles...)) return personAccess } @@ -86,13 +86,13 @@ func canonicalCirclePolicies(circlePolicies []CirclePolicy) []CirclePolicy { for index := range result { result[index].CircleID = strings.ToLower(strings.TrimSpace(result[index].CircleID)) } - if hasCirclePolicy(result, StaffCircleID) { + if hasCirclePolicy(result, MemberCircleID) { return result } return append([]CirclePolicy{{ - CircleID: StaffCircleID, + CircleID: MemberCircleID, DisplayName: "Staff", - WorkspaceDirectoryPath: "/workspace/circles/" + StaffCircleID, + WorkspaceDirectoryPath: "/workspace/circles/" + MemberCircleID, }}, result...) } diff --git a/internal/policy/policy_projection_service_test.go b/internal/policy/policy_projection_service_test.go index 45570af4..d8ad02bc 100644 --- a/internal/policy/policy_projection_service_test.go +++ b/internal/policy/policy_projection_service_test.go @@ -2,7 +2,7 @@ package policy import "testing" -func TestPolicyProjectionGivesStaffToEveryPerson(t *testing.T) { +func TestPolicyProjectionGivesMemberToEveryPerson(t *testing.T) { policyProjection := PolicyProjectionService{}.ReplacePolicyProjectionTransactionally(PolicyDocument{ People: []PersonPolicy{ { @@ -24,8 +24,8 @@ func TestPolicyProjectionGivesStaffToEveryPerson(t *testing.T) { for _, personID := range []string{"person-1", "person-2", "admin-1"} { personAccess := policyProjection.PersonAccessByPersonID[personID] - if !hasTestPolicyString(personAccess.Circles, "staff") { - t.Fatalf("expected %s to have staff circle, got %+v", personID, personAccess.Circles) + if !hasTestPolicyString(personAccess.Circles, "member") { + t.Fatalf("expected %s to have member circle, got %+v", personID, personAccess.Circles) } } } @@ -41,8 +41,8 @@ func TestPolicyProjectionAddsAdminWithoutGrantingCLevel(t *testing.T) { }) personAccess := policyProjection.PersonAccessByPersonID["admin-1"] - if !hasTestPolicyString(personAccess.Circles, "staff") || !hasTestPolicyString(personAccess.Circles, "admin") { - t.Fatalf("expected staff and admin circles, got %+v", personAccess.Circles) + if !hasTestPolicyString(personAccess.Circles, "member") || !hasTestPolicyString(personAccess.Circles, "admin") { + t.Fatalf("expected the member circle and admin circles, got %+v", personAccess.Circles) } if hasTestPolicyString(personAccess.Circles, "c-level") { t.Fatalf("expected executive legacy class not to grant c-level, got %+v", personAccess.Circles) @@ -58,12 +58,12 @@ func TestPolicyProjectionNormalizesExplicitCircles(t *testing.T) { }}, People: []PersonPolicy{{ PersonID: "person-1", - Circles: []string{" Staff ", "Finance", "finance"}, + Circles: []string{" Member ", "Finance", "finance"}, }}, }) personAccess := policyProjection.PersonAccessByPersonID["person-1"] - if len(personAccess.Circles) != 2 || personAccess.Circles[0] != "staff" || personAccess.Circles[1] != "finance" { + if len(personAccess.Circles) != 2 || personAccess.Circles[0] != "member" || personAccess.Circles[1] != "finance" { t.Fatalf("expected normalized unique circles, got %+v", personAccess.Circles) } if len(personAccess.ResourceAccessRules) != 1 { @@ -71,18 +71,18 @@ func TestPolicyProjectionNormalizesExplicitCircles(t *testing.T) { } } -func TestEnsureRequesterDefaultsPreservesStaffInvariant(t *testing.T) { +func TestEnsureRequesterDefaultsPreservesMemberInvariant(t *testing.T) { personAccess := EnsureRequesterDefaults(PersonAccess{ PersonID: "person-1", - Circles: []string{" Finance ", "staff", "finance"}, + Circles: []string{" Finance ", "member", "finance"}, }) - if len(personAccess.Circles) != 2 || personAccess.Circles[0] != "staff" || personAccess.Circles[1] != "finance" { - t.Fatalf("expected staff plus normalized explicit circles, got %+v", personAccess.Circles) + if len(personAccess.Circles) != 2 || personAccess.Circles[0] != "member" || personAccess.Circles[1] != "finance" { + t.Fatalf("expected the member circle plus normalized explicit circles, got %+v", personAccess.Circles) } } -func TestCanonicalizePolicyDocumentWritesStaffToEveryPersonAndCircleList(t *testing.T) { +func TestCanonicalizePolicyDocumentWritesMemberToEveryPersonAndCircleList(t *testing.T) { policyDocument := CanonicalizePolicyDocument(PolicyDocument{ People: []PersonPolicy{ {PersonID: "person-1", Emails: []string{"person@example.com"}, Circles: []string{"Finance"}}, @@ -91,12 +91,12 @@ func TestCanonicalizePolicyDocumentWritesStaffToEveryPersonAndCircleList(t *test Circles: []CirclePolicy{{CircleID: "Finance", DisplayName: "Finance"}}, }) - if !hasTestPolicyCircle(policyDocument.Circles, "staff") { - t.Fatalf("expected canonical policy to include staff circle, got %+v", policyDocument.Circles) + if !hasTestPolicyCircle(policyDocument.Circles, "member") { + t.Fatalf("expected canonical policy to include member circle, got %+v", policyDocument.Circles) } for _, personPolicy := range policyDocument.People { - if !hasTestPolicyString(personPolicy.Circles, "staff") { - t.Fatalf("expected %s to persist staff membership, got %+v", personPolicy.PersonID, personPolicy.Circles) + if !hasTestPolicyString(personPolicy.Circles, "member") { + t.Fatalf("expected %s to persist member membership, got %+v", personPolicy.PersonID, personPolicy.Circles) } } if !hasTestPolicyString(policyDocument.People[1].Circles, "admin") { diff --git a/internal/security/capability_socket_invariant_test.go b/internal/security/capability_socket_invariant_test.go index 2950610e..d49b6238 100644 --- a/internal/security/capability_socket_invariant_test.go +++ b/internal/security/capability_socket_invariant_test.go @@ -37,7 +37,7 @@ func TestVerifyCapabilitySocketInvariantPassesWhenGroupIsNotARequesterGroupAndMo return "blueclaw", 0o660, nil } resolveRequesterGroups := func() ([]string, error) { - return []string{"bc_shared", "bc_circle_staff", "bc_person_abcd1234"}, nil + return []string{"bc_shared", "bc_circle_member", "bc_person_abcd1234"}, nil } result, verifyError := VerifyCapabilitySocketInvariant(socketPath, resolveGroup, resolveRequesterGroups) @@ -78,7 +78,7 @@ func TestVerifyCapabilitySocketInvariantFailsWhenRequesterGroupOwnsSocket(t *tes return "bc_shared", 0o660, nil } resolveRequesterGroups := func() ([]string, error) { - return []string{"bc_shared", "bc_circle_staff"}, nil + return []string{"bc_shared", "bc_circle_member"}, nil } _, verifyError := VerifyCapabilitySocketInvariant(socketPath, resolveGroup, resolveRequesterGroups) diff --git a/internal/security/posix_identity.go b/internal/security/posix_identity.go index 6ae45569..64e79e51 100644 --- a/internal/security/posix_identity.go +++ b/internal/security/posix_identity.go @@ -190,7 +190,7 @@ func POSIXStateForPolicy(policyDocument policy.PolicyDocument, workspaceRootPath Group: groupName, ModeText: "2770", }) - if circleID == policy.StaffCircleID { + if circleID == policy.MemberCircleID { state.Directories = append(state.Directories, POSIXDirectory{ Path: strings.TrimRight(circleWorkspacePath, "/") + "/sites", Owner: blueclawServiceUserName, @@ -319,7 +319,7 @@ func linuxNameValueWithSuffix(normalizedValue string, value string, maximumLengt } func effectivePersonCirclesForPOSIX(personPolicy policy.PersonPolicy) []string { - circles := append([]string{policy.StaffCircleID}, personPolicy.Circles...) + circles := append([]string{policy.MemberCircleID}, personPolicy.Circles...) if personPolicy.IsAdmin { circles = append(circles, policy.AdminCircleID) } @@ -330,15 +330,15 @@ func circlePoliciesWithStaffDefault(circlePolicies []policy.CirclePolicy, worksp hasStaffCircle := false result := append([]policy.CirclePolicy{}, circlePolicies...) for _, circlePolicy := range circlePolicies { - if strings.ToLower(strings.TrimSpace(circlePolicy.CircleID)) == policy.StaffCircleID { + if strings.ToLower(strings.TrimSpace(circlePolicy.CircleID)) == policy.MemberCircleID { hasStaffCircle = true break } } if !hasStaffCircle { result = append(result, policy.CirclePolicy{ - CircleID: policy.StaffCircleID, - WorkspaceDirectoryPath: workspaceRootPath + "/circles/" + policy.StaffCircleID, + CircleID: policy.MemberCircleID, + WorkspaceDirectoryPath: workspaceRootPath + "/circles/" + policy.MemberCircleID, }) } return result diff --git a/internal/security/posix_identity_test.go b/internal/security/posix_identity_test.go index f7aa2cfc..8bab3e0c 100644 --- a/internal/security/posix_identity_test.go +++ b/internal/security/posix_identity_test.go @@ -39,7 +39,7 @@ func TestLinuxIdentityNamesAvoidLossyNormalizationCollisions(t *testing.T) { func TestExecutionIdentityOmitsAdminGroupForRawTerminal(t *testing.T) { identity := ExecutionIdentityForPersonAccess(policy.PersonAccess{ PersonID: "person-1", - Circles: []string{"staff", "admin"}, + Circles: []string{"member", "admin"}, }, "/workspace") if identity.UserName != "bc_person_person-1" { @@ -52,13 +52,13 @@ func TestExecutionIdentityOmitsAdminGroupForRawTerminal(t *testing.T) { } } -func TestExecutionIdentityAddsStaffGroupForRequester(t *testing.T) { +func TestExecutionIdentityAddsMemberGroupForRequester(t *testing.T) { identity := ExecutionIdentityForPersonAccess(policy.PersonAccess{ PersonID: "person-1", }, "/workspace") - if !hasTestString(identity.SupplementaryGroupNames, "bc_circle_staff") { - t.Fatalf("expected requester identity to include staff group, got %+v", identity.SupplementaryGroupNames) + if !hasTestString(identity.SupplementaryGroupNames, "bc_circle_member") { + t.Fatalf("expected requester identity to include member group, got %+v", identity.SupplementaryGroupNames) } } @@ -92,24 +92,24 @@ func TestPOSIXStateForPolicyProjectsWorkspaceDirectories(t *testing.T) { if !hasPOSIXDirectory(state, "/workspace/circles", "blueclaw", "blueclaw", "0711") { t.Fatalf("expected circles parent traversal directory, got %+v", state.Directories) } - if !hasPOSIXDirectory(state, "/workspace/circles/staff", "blueclaw", "bc_circle_staff", "2770") { - t.Fatalf("expected default staff circle POSIX directory, got %+v", state.Directories) + if !hasPOSIXDirectory(state, "/workspace/circles/member", "blueclaw", "bc_circle_member", "2770") { + t.Fatalf("expected default member circle POSIX directory, got %+v", state.Directories) } - if !hasPOSIXDirectory(state, "/workspace/circles/staff/sites", "blueclaw", "bc_circle_staff", "2770") { - t.Fatalf("expected staff site workspace directory, got %+v", state.Directories) + if !hasPOSIXDirectory(state, "/workspace/circles/member/sites", "blueclaw", "bc_circle_member", "2770") { + t.Fatalf("expected member site workspace directory, got %+v", state.Directories) } if !hasPOSIXDirectory(state, "/workspace/circles/finance", "blueclaw", "bc_circle_finance", "2770") { t.Fatalf("expected circle POSIX directory, got %+v", state.Directories) } - if !hasPOSIXGroup(state, "bc_circle_staff") { - t.Fatalf("expected staff circle group, got %+v", state.Groups) + if !hasPOSIXGroup(state, "bc_circle_member") { + t.Fatalf("expected member circle group, got %+v", state.Groups) } - if !hasPOSIXUserGroup(state, "bc_person_person-1", "bc_circle_staff") { - t.Fatalf("expected every requester POSIX user to be staff member, got %+v", state.Users) + if !hasPOSIXUserGroup(state, "bc_person_person-1", "bc_circle_member") { + t.Fatalf("expected every requester POSIX user to be member member, got %+v", state.Users) } } -func TestPOSIXStateForPolicyGivesEveryPersonStaffAccess(t *testing.T) { +func TestPOSIXStateForPolicyGivesEveryPersonMemberAccess(t *testing.T) { state := POSIXStateForPolicy(policy.PolicyDocument{ People: []policy.PersonPolicy{ {PersonID: "person-1"}, @@ -118,18 +118,18 @@ func TestPOSIXStateForPolicyGivesEveryPersonStaffAccess(t *testing.T) { }, }, "/workspace") - if !hasPOSIXDirectory(state, "/workspace/circles/staff", "blueclaw", "bc_circle_staff", "2770") { - t.Fatalf("expected default staff circle directory, got %+v", state.Directories) + if !hasPOSIXDirectory(state, "/workspace/circles/member", "blueclaw", "bc_circle_member", "2770") { + t.Fatalf("expected default member circle directory, got %+v", state.Directories) } - if !hasPOSIXDirectory(state, "/workspace/circles/staff/sites", "blueclaw", "bc_circle_staff", "2770") { - t.Fatalf("expected default staff sites directory, got %+v", state.Directories) + if !hasPOSIXDirectory(state, "/workspace/circles/member/sites", "blueclaw", "bc_circle_member", "2770") { + t.Fatalf("expected default member sites directory, got %+v", state.Directories) } - if !hasPOSIXGroup(state, "bc_circle_staff") { - t.Fatalf("expected staff circle group, got %+v", state.Groups) + if !hasPOSIXGroup(state, "bc_circle_member") { + t.Fatalf("expected member circle group, got %+v", state.Groups) } for _, userName := range []string{"bc_person_person-1", "bc_person_person-2", "bc_person_admin-1"} { - if !hasPOSIXUserGroup(state, userName, "bc_circle_staff") { - t.Fatalf("expected %s to be a staff group member, got %+v", userName, state.Users) + if !hasPOSIXUserGroup(state, userName, "bc_circle_member") { + t.Fatalf("expected %s to be a member group member, got %+v", userName, state.Users) } } } diff --git a/internal/security/posix_synchronizer_test.go b/internal/security/posix_synchronizer_test.go index 732f9f2c..ff90f171 100644 --- a/internal/security/posix_synchronizer_test.go +++ b/internal/security/posix_synchronizer_test.go @@ -25,7 +25,7 @@ func TestPOSIXSynchronizerPassesComputedStateDocumentToHelper(t *testing.T) { policyDocument := policy.PolicyDocument{ People: []policy.PersonPolicy{{ PersonID: "id_22120f1e_432e6dde", - Circles: []string{policy.StaffCircleID}, + Circles: []string{policy.MemberCircleID}, }}, } document, errorValue := json.Marshal(policyDocument) @@ -52,8 +52,8 @@ func TestPOSIXSynchronizerPassesComputedStateDocumentToHelper(t *testing.T) { if errorValue := json.Unmarshal(observedDocument, &state); errorValue != nil { t.Fatal(errorValue) } - if !containsPOSIXDirectory(state, "/workspace/circles/staff/sites", "blueclaw", "bc_circle_staff", "2770") { - t.Fatalf("expected staff sites directory in state, got %+v", state.Directories) + if !containsPOSIXDirectory(state, "/workspace/circles/member/sites", "blueclaw", "bc_circle_member", "2770") { + t.Fatalf("expected member sites directory in state, got %+v", state.Directories) } } @@ -119,8 +119,8 @@ esac if errorValue := json.Unmarshal(observedDocument, &state); errorValue != nil { t.Fatal(errorValue) } - if !hasPOSIXUserGroup(state, "bc_person_person-1", "bc_circle_staff") { - t.Fatalf("expected requester to inherit staff group, got %+v", state.Users) + if !hasPOSIXUserGroup(state, "bc_person_person-1", "bc_circle_member") { + t.Fatalf("expected requester to inherit member group, got %+v", state.Users) } if !hasPOSIXUserGroup(state, "bc_person_person-1", "bc_circle_finance") { t.Fatalf("expected requester finance circle group, got %+v", state.Users) @@ -128,8 +128,8 @@ esac if hasPOSIXUserGroup(state, "bc_person_person-1", "bc_circle_admin") { t.Fatalf("expected requester POSIX identity to omit admin group, got %+v", state.Users) } - if !containsPOSIXDirectory(state, "/workspace/circles/staff/sites", "blueclaw", "bc_circle_staff", "2770") { - t.Fatalf("expected staff sites directory, got %+v", state.Directories) + if !containsPOSIXDirectory(state, "/workspace/circles/member/sites", "blueclaw", "bc_circle_member", "2770") { + t.Fatalf("expected member sites directory, got %+v", state.Directories) } if !containsPOSIXDirectory(state, "/workspace/circles/finance", "blueclaw", "bc_circle_finance", "2770") { t.Fatalf("expected requester circle directory, got %+v", state.Directories) diff --git a/tests/integration/posix_separation_test.go b/tests/integration/posix_separation_test.go index de611b03..1e13e964 100644 --- a/tests/integration/posix_separation_test.go +++ b/tests/integration/posix_separation_test.go @@ -31,7 +31,7 @@ func TestTwoPeopleGetSeparatePOSIXWorkspaces(t *testing.T) { } requireBlueclawServiceAccount(t) - removeProjectedIdentitiesAfter(t, []string{"person-one", "person-two"}, []string{"staff"}) + removeProjectedIdentitiesAfter(t, []string{"person-one", "person-two"}, []string{"member"}) workspaceRootPath := traversableTempDir(t) policyPath := writeTwoPersonPolicy(t) terminalConfiguration := config.TerminalConfiguration{ @@ -44,7 +44,7 @@ func TestTwoPeopleGetSeparatePOSIXWorkspaces(t *testing.T) { provisioner := security.NewPOSIXRequesterWorkspaceProvisioner(security.NewPOSIXSynchronizer(terminalConfiguration, policyPath)) for _, personID := range []string{"person-one", "person-two"} { - personAccess := policy.PersonAccess{PersonID: personID, Circles: []string{"staff"}} + personAccess := policy.PersonAccess{PersonID: personID, Circles: []string{"member"}} if errorValue := provisioner.ProvisionRequesterWorkspace(context.Background(), personAccess, workspaceRootPath); errorValue != nil { t.Fatalf("expected %s to be provisioned: %v", personID, errorValue) } @@ -96,9 +96,9 @@ func ancestorsBelowTemporaryRoot(directoryPath string, temporaryRootPath string) func writeTwoPersonPolicy(t *testing.T) string { t.Helper() document := `{"people":[ - {"personID":"person-one","displayName":"One","emails":["one@example.com"],"securityLevelName":"member","securityLevelRank":50,"grantedClasses":["internal"],"circles":["staff"]}, - {"personID":"person-two","displayName":"Two","emails":["two@example.com"],"securityLevelName":"member","securityLevelRank":50,"grantedClasses":["internal"],"circles":["staff"]} - ],"circles":[{"circleID":"staff","displayName":"Staff"}]}` + {"personID":"person-one","displayName":"One","emails":["one@example.com"],"securityLevelName":"member","securityLevelRank":50,"grantedClasses":["internal"],"circles":["member"]}, + {"personID":"person-two","displayName":"Two","emails":["two@example.com"],"securityLevelName":"member","securityLevelRank":50,"grantedClasses":["internal"],"circles":["member"]} + ],"circles":[{"circleID":"member","displayName":"Member"}]}` policyPath := filepath.Join(t.TempDir(), "policy.json") if errorValue := os.WriteFile(policyPath, []byte(document), 0o600); errorValue != nil { t.Fatal(errorValue)