From c479d6024342f07b42ea34a6333608625490e1d8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 15:42:53 +0000 Subject: [PATCH 1/5] Initial plan From 05ddf4ef96a9b19c57898abaa9211da6970daf06 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 15:51:27 +0000 Subject: [PATCH 2/5] Add unassign_from_user tool to safe outputs toolset - Add unassign_from_user tool definition to compile-time schema - Add handler registry entries for assign_to_user and unassign_from_user - Add comprehensive tests for both handlers - Tool supports max limit, allowed users, target, and cross-repo operations Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_safe_outputs_config.go | 26 +++ .../compiler_safe_outputs_config_test.go | 151 ++++++++++++++++++ pkg/workflow/js/safe_outputs_tools.json | 32 ++++ 3 files changed, 209 insertions(+) diff --git a/pkg/workflow/compiler_safe_outputs_config.go b/pkg/workflow/compiler_safe_outputs_config.go index 777745c25e1..66b3662510b 100644 --- a/pkg/workflow/compiler_safe_outputs_config.go +++ b/pkg/workflow/compiler_safe_outputs_config.go @@ -533,6 +533,32 @@ var handlerRegistry = map[string]handlerBuilder{ } return builder.Build() }, + "assign_to_user": func(cfg *SafeOutputsConfig) map[string]any { + if cfg.AssignToUser == nil { + return nil + } + c := cfg.AssignToUser + return newHandlerConfigBuilder(). + AddIfPositive("max", c.Max). + AddStringSlice("allowed", c.Allowed). + AddIfNotEmpty("target", c.Target). + AddIfNotEmpty("target-repo", c.TargetRepoSlug). + AddStringSlice("allowed_repos", c.AllowedRepos). + Build() + }, + "unassign_from_user": func(cfg *SafeOutputsConfig) map[string]any { + if cfg.UnassignFromUser == nil { + return nil + } + c := cfg.UnassignFromUser + return newHandlerConfigBuilder(). + AddIfPositive("max", c.Max). + AddStringSlice("allowed", c.Allowed). + AddIfNotEmpty("target", c.Target). + AddIfNotEmpty("target-repo", c.TargetRepoSlug). + AddStringSlice("allowed_repos", c.AllowedRepos). + Build() + }, "create_project_status_update": func(cfg *SafeOutputsConfig) map[string]any { if cfg.CreateProjectStatusUpdates == nil { return nil diff --git a/pkg/workflow/compiler_safe_outputs_config_test.go b/pkg/workflow/compiler_safe_outputs_config_test.go index ab9edbf61dc..3885af3fd1c 100644 --- a/pkg/workflow/compiler_safe_outputs_config_test.go +++ b/pkg/workflow/compiler_safe_outputs_config_test.go @@ -870,3 +870,154 @@ func TestCreatePullRequestBaseBranch(t *testing.T) { }) } } + +// TestHandlerConfigAssignToUser tests assign_to_user configuration +func TestHandlerConfigAssignToUser(t *testing.T) { + compiler := NewCompiler() + + workflowData := &WorkflowData{ + Name: "Test Workflow", + SafeOutputs: &SafeOutputsConfig{ + AssignToUser: &AssignToUserConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 5, + }, + SafeOutputTargetConfig: SafeOutputTargetConfig{ + Target: "issues", + TargetRepoSlug: "org/target-repo", + AllowedRepos: []string{"org/repo1", "org/repo2"}, + }, + Allowed: []string{"user1", "user2", "copilot"}, + }, + }, + } + + var steps []string + compiler.addHandlerManagerConfigEnvVar(&steps, workflowData) + + // Extract and validate JSON + for _, step := range steps { + if strings.Contains(step, "GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG") { + parts := strings.Split(step, "GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: ") + if len(parts) == 2 { + jsonStr := strings.TrimSpace(parts[1]) + jsonStr = strings.Trim(jsonStr, "\"") + jsonStr = strings.ReplaceAll(jsonStr, "\\\"", "\"") + + var config map[string]map[string]any + err := json.Unmarshal([]byte(jsonStr), &config) + require.NoError(t, err, "Handler config JSON should be valid") + + assignConfig, ok := config["assign_to_user"] + require.True(t, ok, "Should have assign_to_user handler") + + // Check max + max, ok := assignConfig["max"] + require.True(t, ok, "Should have max field") + assert.Equal(t, float64(5), max, "Max should be 5") + + // Check allowed users + allowed, ok := assignConfig["allowed"] + require.True(t, ok, "Should have allowed field") + allowedSlice, ok := allowed.([]any) + require.True(t, ok, "Allowed should be an array") + assert.Len(t, allowedSlice, 3, "Should have 3 allowed users") + assert.Equal(t, "user1", allowedSlice[0]) + assert.Equal(t, "user2", allowedSlice[1]) + assert.Equal(t, "copilot", allowedSlice[2]) + + // Check target + target, ok := assignConfig["target"] + require.True(t, ok, "Should have target field") + assert.Equal(t, "issues", target) + + // Check target-repo + targetRepo, ok := assignConfig["target-repo"] + require.True(t, ok, "Should have target-repo field") + assert.Equal(t, "org/target-repo", targetRepo) + + // Check allowed_repos + allowedRepos, ok := assignConfig["allowed_repos"] + require.True(t, ok, "Should have allowed_repos field") + allowedReposSlice, ok := allowedRepos.([]any) + require.True(t, ok, "Allowed repos should be an array") + assert.Len(t, allowedReposSlice, 2, "Should have 2 allowed repos") + } + } + } +} + +// TestHandlerConfigUnassignFromUser tests unassign_from_user configuration +func TestHandlerConfigUnassignFromUser(t *testing.T) { + compiler := NewCompiler() + + workflowData := &WorkflowData{ + Name: "Test Workflow", + SafeOutputs: &SafeOutputsConfig{ + UnassignFromUser: &UnassignFromUserConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{ + Max: 10, + }, + SafeOutputTargetConfig: SafeOutputTargetConfig{ + Target: "issues", + TargetRepoSlug: "org/target-repo", + AllowedRepos: []string{"org/repo1"}, + }, + Allowed: []string{"githubactionagent", "bot-user"}, + }, + }, + } + + var steps []string + compiler.addHandlerManagerConfigEnvVar(&steps, workflowData) + + // Extract and validate JSON + for _, step := range steps { + if strings.Contains(step, "GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG") { + parts := strings.Split(step, "GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: ") + if len(parts) == 2 { + jsonStr := strings.TrimSpace(parts[1]) + jsonStr = strings.Trim(jsonStr, "\"") + jsonStr = strings.ReplaceAll(jsonStr, "\\\"", "\"") + + var config map[string]map[string]any + err := json.Unmarshal([]byte(jsonStr), &config) + require.NoError(t, err, "Handler config JSON should be valid") + + unassignConfig, ok := config["unassign_from_user"] + require.True(t, ok, "Should have unassign_from_user handler") + + // Check max + max, ok := unassignConfig["max"] + require.True(t, ok, "Should have max field") + assert.Equal(t, float64(10), max, "Max should be 10") + + // Check allowed users + allowed, ok := unassignConfig["allowed"] + require.True(t, ok, "Should have allowed field") + allowedSlice, ok := allowed.([]any) + require.True(t, ok, "Allowed should be an array") + assert.Len(t, allowedSlice, 2, "Should have 2 allowed users") + assert.Equal(t, "githubactionagent", allowedSlice[0]) + assert.Equal(t, "bot-user", allowedSlice[1]) + + // Check target + target, ok := unassignConfig["target"] + require.True(t, ok, "Should have target field") + assert.Equal(t, "issues", target) + + // Check target-repo + targetRepo, ok := unassignConfig["target-repo"] + require.True(t, ok, "Should have target-repo field") + assert.Equal(t, "org/target-repo", targetRepo) + + // Check allowed_repos + allowedRepos, ok := unassignConfig["allowed_repos"] + require.True(t, ok, "Should have allowed_repos field") + allowedReposSlice, ok := allowedRepos.([]any) + require.True(t, ok, "Allowed repos should be an array") + assert.Len(t, allowedReposSlice, 1, "Should have 1 allowed repo") + } + } + } +} diff --git a/pkg/workflow/js/safe_outputs_tools.json b/pkg/workflow/js/safe_outputs_tools.json index 5f51146f2b4..b35bb9867cd 100644 --- a/pkg/workflow/js/safe_outputs_tools.json +++ b/pkg/workflow/js/safe_outputs_tools.json @@ -561,6 +561,38 @@ "additionalProperties": false } }, + { + "name": "unassign_from_user", + "description": "Remove one or more assignees from an issue. Use this to unassign users when work is being reassigned or removed from their queue.", + "inputSchema": { + "type": "object", + "properties": { + "issue_number": { + "type": [ + "number", + "string" + ], + "description": "Issue number to unassign users from. This is the numeric ID from the GitHub URL (e.g., 543 in github.com/owner/repo/issues/543). If omitted, uses the issue that triggered this workflow." + }, + "assignees": { + "type": "array", + "items": { + "type": "string" + }, + "description": "GitHub usernames to unassign from the issue (e.g., ['octocat', 'mona'])." + }, + "assignee": { + "type": "string", + "description": "Single GitHub username to unassign. Use 'assignees' array for multiple users." + }, + "repo": { + "type": "string", + "description": "Target repository in 'owner/repo' format. If omitted, uses the current repository. Must be in allowed-repos list if specified." + } + }, + "additionalProperties": false + } + }, { "name": "update_issue", "description": "Update an existing GitHub issue's title, body, labels, assignees, or milestone WITHOUT closing it. This tool is primarily for editing issue metadata and content. While it supports changing status between 'open' and 'closed', use close_issue instead when you want to close an issue with a closing comment. Body updates support replacing, appending to, prepending content, or updating a per-run \"island\" section.", From 3b46ea003e5f48e7ed7c7927172a5b8d8b556b4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 15:53:21 +0000 Subject: [PATCH 3/5] Recompile workflows with unassign_from_user tool Update workflow lock files to include the new unassign_from_user tool in schema and handler configs. This enables workflows like smoke-codex to properly remove assignees from PRs during cleanup. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../workflows/code-scanning-fixer.lock.yml | 2 +- .github/workflows/code-simplifier.lock.yml | 2 +- .../daily-assign-issue-to-user.lock.yml | 2 +- .github/workflows/daily-doc-updater.lock.yml | 2 +- .../workflows/functional-pragmatist.lock.yml | 2 +- .../github-mcp-tools-report.lock.yml | 2 +- .github/workflows/poem-bot.lock.yml | 2 +- .github/workflows/q.lock.yml | 2 +- .github/workflows/refiner.lock.yml | 2 +- .github/workflows/smoke-codex.lock.yml | 34 ++++++++++++++++++- .../workflows/technical-doc-writer.lock.yml | 2 +- .github/workflows/tidy.lock.yml | 2 +- .github/workflows/unbloat-docs.lock.yml | 2 +- 13 files changed, 45 insertions(+), 13 deletions(-) diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml index a800dbc5700..e2c5de98f2f 100644 --- a/.github/workflows/code-scanning-fixer.lock.yml +++ b/.github/workflows/code-scanning-fixer.lock.yml @@ -1294,7 +1294,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"allowed\":[\"agentic-campaign\",\"z_campaign_security-alert-burndown\"]},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"expires\":48,\"labels\":[\"security\",\"automated-fix\",\"agentic-campaign\",\"z_campaign_security-alert-burndown\"],\"max\":1,\"max_patch_size\":1024,\"title_prefix\":\"[code-scanning-fix] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"allowed\":[\"agentic-campaign\",\"z_campaign_security-alert-burndown\"]},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"expires\":48,\"labels\":[\"security\",\"automated-fix\",\"agentic-campaign\",\"z_campaign_security-alert-burndown\"],\"max\":1,\"max_patch_size\":1024,\"reviewers\":[\"copilot\"],\"title_prefix\":\"[code-scanning-fix] \"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/code-simplifier.lock.yml b/.github/workflows/code-simplifier.lock.yml index a978bc28b9f..369c9ed24f9 100644 --- a/.github/workflows/code-simplifier.lock.yml +++ b/.github/workflows/code-simplifier.lock.yml @@ -1135,7 +1135,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"expires\":24,\"labels\":[\"refactoring\",\"code-quality\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"title_prefix\":\"[code-simplifier] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"expires\":24,\"labels\":[\"refactoring\",\"code-quality\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"reviewers\":[\"copilot\"],\"title_prefix\":\"[code-simplifier] \"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/daily-assign-issue-to-user.lock.yml b/.github/workflows/daily-assign-issue-to-user.lock.yml index 506e19d8df5..174be9c3e2f 100644 --- a/.github/workflows/daily-assign-issue-to-user.lock.yml +++ b/.github/workflows/daily-assign-issue-to-user.lock.yml @@ -1059,7 +1059,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"target\":\"*\"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"target\":\"*\"},\"assign_to_user\":{\"max\":1,\"target\":\"*\"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index 72638dc8cea..74e81cd4684 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -1201,7 +1201,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"auto_merge\":true,\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":24,\"labels\":[\"documentation\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"title_prefix\":\"[docs] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"auto_merge\":true,\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":24,\"labels\":[\"documentation\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"reviewers\":[\"copilot\"],\"title_prefix\":\"[docs] \"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/functional-pragmatist.lock.yml b/.github/workflows/functional-pragmatist.lock.yml index 891f4d8e780..25b75912b52 100644 --- a/.github/workflows/functional-pragmatist.lock.yml +++ b/.github/workflows/functional-pragmatist.lock.yml @@ -1088,7 +1088,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"expires\":24,\"labels\":[\"refactoring\",\"functional\",\"immutability\",\"code-quality\"],\"max\":1,\"max_patch_size\":1024,\"title_prefix\":\"[fp-enhancer] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"expires\":24,\"labels\":[\"refactoring\",\"functional\",\"immutability\",\"code-quality\"],\"max\":1,\"max_patch_size\":1024,\"reviewers\":[\"copilot\"],\"title_prefix\":\"[fp-enhancer] \"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index 2126695efa1..0e1fc81d6f8 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -1235,7 +1235,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":168,\"fallback_to_issue\":true,\"max\":1},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":48,\"labels\":[\"documentation\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"title_prefix\":\"[mcp-tools] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"audits\",\"close_older_discussions\":true,\"expires\":168,\"fallback_to_issue\":true,\"max\":1},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":48,\"labels\":[\"documentation\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"reviewers\":[\"copilot\"],\"title_prefix\":\"[mcp-tools] \"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 9ad8fc90f0b..4a3fd315376 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1789,7 +1789,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\"},\"add_labels\":{\"allowed\":[\"poetry\",\"creative\",\"automation\",\"ai-generated\",\"epic\",\"haiku\",\"sonnet\",\"limerick\"],\"max\":5},\"close_pull_request\":{\"max\":2,\"required_labels\":[\"poetry\",\"automation\"],\"required_title_prefix\":\"[🎨 POETRY]\",\"target\":\"*\"},\"create_discussion\":{\"category\":\"general\",\"close_older_discussions\":true,\"expires\":168,\"fallback_to_issue\":true,\"labels\":[\"poetry\",\"automation\",\"ai-generated\"],\"max\":2,\"title_prefix\":\"[📜 POETRY] \"},\"create_issue\":{\"expires\":48,\"group\":true,\"labels\":[\"poetry\",\"automation\",\"ai-generated\"],\"max\":2,\"title_prefix\":\"[🎭 POEM-BOT] \"},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":48,\"labels\":[\"poetry\",\"automation\",\"creative-writing\"],\"max\":1,\"max_patch_size\":1024,\"title_prefix\":\"[🎨 POETRY] \"},\"create_pull_request_review_comment\":{\"max\":2,\"side\":\"RIGHT\"},\"link_sub_issue\":{\"max\":3,\"parent_required_labels\":[\"poetry\",\"epic\"],\"parent_title_prefix\":\"[🎭 POEM-BOT]\",\"sub_required_labels\":[\"poetry\"],\"sub_title_prefix\":\"[🎭 POEM-BOT]\"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"base_branch\":\"${{ github.ref_name }}\",\"if_no_changes\":\"warn\",\"max_patch_size\":1024},\"update_issue\":{\"allow_body\":true,\"allow_status\":true,\"allow_title\":true,\"max\":2,\"target\":\"*\"}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":3,\"target\":\"*\"},\"add_labels\":{\"allowed\":[\"poetry\",\"creative\",\"automation\",\"ai-generated\",\"epic\",\"haiku\",\"sonnet\",\"limerick\"],\"max\":5},\"close_pull_request\":{\"max\":2,\"required_labels\":[\"poetry\",\"automation\"],\"required_title_prefix\":\"[🎨 POETRY]\",\"target\":\"*\"},\"create_discussion\":{\"category\":\"general\",\"close_older_discussions\":true,\"expires\":168,\"fallback_to_issue\":true,\"labels\":[\"poetry\",\"automation\",\"ai-generated\"],\"max\":2,\"title_prefix\":\"[📜 POETRY] \"},\"create_issue\":{\"expires\":48,\"group\":true,\"labels\":[\"poetry\",\"automation\",\"ai-generated\"],\"max\":2,\"title_prefix\":\"[🎭 POEM-BOT] \"},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":48,\"labels\":[\"poetry\",\"automation\",\"creative-writing\"],\"max\":1,\"max_patch_size\":1024,\"reviewers\":[\"copilot\"],\"title_prefix\":\"[🎨 POETRY] \"},\"create_pull_request_review_comment\":{\"max\":2,\"side\":\"RIGHT\"},\"link_sub_issue\":{\"max\":3,\"parent_required_labels\":[\"poetry\",\"epic\"],\"parent_title_prefix\":\"[🎭 POEM-BOT]\",\"sub_required_labels\":[\"poetry\"],\"sub_title_prefix\":\"[🎭 POEM-BOT]\"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"base_branch\":\"${{ github.ref_name }}\",\"if_no_changes\":\"warn\",\"max_patch_size\":1024},\"update_issue\":{\"allow_body\":true,\"allow_status\":true,\"allow_title\":true,\"max\":2,\"target\":\"*\"}}" GH_AW_SAFE_OUTPUTS_STAGED: "true" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 4c2d22a8aa9..cba3b48de9e 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1352,7 +1352,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":48,\"if_no_changes\":\"ignore\",\"labels\":[\"automation\",\"workflow-optimization\"],\"max\":1,\"max_patch_size\":1024,\"title_prefix\":\"[q] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":48,\"if_no_changes\":\"ignore\",\"labels\":[\"automation\",\"workflow-optimization\"],\"max\":1,\"max_patch_size\":1024,\"reviewers\":[\"copilot\"],\"title_prefix\":\"[q] \"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/refiner.lock.yml b/.github/workflows/refiner.lock.yml index 17e3ce15b42..5ec21e81be4 100644 --- a/.github/workflows/refiner.lock.yml +++ b/.github/workflows/refiner.lock.yml @@ -1164,7 +1164,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"labels\":[\"automation\",\"refine-improvements\"],\"max\":1,\"max_patch_size\":1024,\"title_prefix\":\"[refiner] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"labels\":[\"automation\",\"refine-improvements\"],\"max\":1,\"max_patch_size\":1024,\"reviewers\":[\"copilot\"],\"title_prefix\":\"[refiner] \"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index d4c0052c5d0..c58ab085d4c 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -369,6 +369,38 @@ jobs: }, "name": "remove_labels" }, + { + "description": "Remove one or more assignees from an issue. Use this to unassign users when work is being reassigned or removed from their queue.", + "inputSchema": { + "additionalProperties": false, + "properties": { + "assignee": { + "description": "Single GitHub username to unassign. Use 'assignees' array for multiple users.", + "type": "string" + }, + "assignees": { + "description": "GitHub usernames to unassign from the issue (e.g., ['octocat', 'mona']).", + "items": { + "type": "string" + }, + "type": "array" + }, + "issue_number": { + "description": "Issue number to unassign users from. This is the numeric ID from the GitHub URL (e.g., 543 in github.com/owner/repo/issues/543). If omitted, uses the issue that triggered this workflow.", + "type": [ + "number", + "string" + ] + }, + "repo": { + "description": "Target repository in 'owner/repo' format. If omitted, uses the current repository. Must be in allowed-repos list if specified.", + "type": "string" + } + }, + "type": "object" + }, + "name": "unassign_from_user" + }, { "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", "inputSchema": { @@ -1497,7 +1529,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":2},\"add_labels\":{\"allowed\":[\"smoke-codex\"]},\"create_issue\":{\"close_older_issues\":true,\"expires\":2,\"max\":1},\"hide_comment\":{\"max\":5},\"missing_data\":{},\"missing_tool\":{},\"remove_labels\":{\"allowed\":[\"smoke\"]}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":2},\"add_labels\":{\"allowed\":[\"smoke-codex\"]},\"create_issue\":{\"close_older_issues\":true,\"expires\":2,\"max\":1},\"hide_comment\":{\"max\":5},\"missing_data\":{},\"missing_tool\":{},\"remove_labels\":{\"allowed\":[\"smoke\"]},\"unassign_from_user\":{\"allowed\":[\"githubactionagent\"],\"max\":1}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index f8fd5165101..aad306fb285 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -1224,7 +1224,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":48,\"labels\":[\"documentation\"],\"max\":1,\"max_patch_size\":1024,\"title_prefix\":\"[docs] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":48,\"labels\":[\"documentation\"],\"max\":1,\"max_patch_size\":1024,\"reviewers\":[\"copilot\"],\"title_prefix\":\"[docs] \"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 3cd783659b7..fd988baeac8 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -1251,7 +1251,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":48,\"labels\":[\"automation\",\"maintenance\"],\"max\":1,\"max_patch_size\":1024,\"title_prefix\":\"[tidy] \"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"base_branch\":\"${{ github.ref_name }}\",\"if_no_changes\":\"warn\",\"max_patch_size\":1024}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"draft\":false,\"expires\":48,\"labels\":[\"automation\",\"maintenance\"],\"max\":1,\"max_patch_size\":1024,\"reviewers\":[\"copilot\"],\"title_prefix\":\"[tidy] \"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"base_branch\":\"${{ github.ref_name }}\",\"if_no_changes\":\"warn\",\"max_patch_size\":1024}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index be9961b19ab..414b2480155 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1428,7 +1428,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request\":{\"auto_merge\":true,\"base_branch\":\"${{ github.ref_name }}\",\"draft\":true,\"expires\":48,\"fallback_as_issue\":false,\"labels\":[\"documentation\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"title_prefix\":\"[docs] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"create_pull_request\":{\"auto_merge\":true,\"base_branch\":\"${{ github.ref_name }}\",\"draft\":true,\"expires\":48,\"fallback_as_issue\":false,\"labels\":[\"documentation\",\"automation\"],\"max\":1,\"max_patch_size\":1024,\"reviewers\":[\"copilot\"],\"title_prefix\":\"[docs] \"},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | From cf4de12e61e21ccaf733ef9a091ce7d7e72ec0a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 15:55:35 +0000 Subject: [PATCH 4/5] Add unassign_from_user to expectedTools test list --- pkg/workflow/safe_outputs_tools_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/workflow/safe_outputs_tools_test.go b/pkg/workflow/safe_outputs_tools_test.go index 41dd78be1c5..c4fbcbc16cb 100644 --- a/pkg/workflow/safe_outputs_tools_test.go +++ b/pkg/workflow/safe_outputs_tools_test.go @@ -326,6 +326,7 @@ func TestGetSafeOutputsToolsJSON(t *testing.T) { "assign_milestone", "assign_to_agent", "assign_to_user", + "unassign_from_user", "update_issue", "update_pull_request", "push_to_pull_request_branch", From 839e08dbfc9e07b3e53d7db5b7e54eda577b5279 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 16 Feb 2026 16:19:39 +0000 Subject: [PATCH 5/5] Fix testifylint float comparison issues Use assert.InDelta instead of assert.Equal for float64 comparisons in test assertions. This follows best practices for floating-point comparisons and resolves testifylint warnings. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_safe_outputs_config_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/workflow/compiler_safe_outputs_config_test.go b/pkg/workflow/compiler_safe_outputs_config_test.go index 3885af3fd1c..7deef8ac5a1 100644 --- a/pkg/workflow/compiler_safe_outputs_config_test.go +++ b/pkg/workflow/compiler_safe_outputs_config_test.go @@ -914,7 +914,7 @@ func TestHandlerConfigAssignToUser(t *testing.T) { // Check max max, ok := assignConfig["max"] require.True(t, ok, "Should have max field") - assert.Equal(t, float64(5), max, "Max should be 5") + assert.InDelta(t, 5.0, max, 0.001, "Max should be 5") // Check allowed users allowed, ok := assignConfig["allowed"] @@ -990,7 +990,7 @@ func TestHandlerConfigUnassignFromUser(t *testing.T) { // Check max max, ok := unassignConfig["max"] require.True(t, ok, "Should have max field") - assert.Equal(t, float64(10), max, "Max should be 10") + assert.InDelta(t, 10.0, max, 0.001, "Max should be 10") // Check allowed users allowed, ok := unassignConfig["allowed"]