diff --git a/docs/src/content/docs/guides/maintaining-repos.md b/docs/src/content/docs/guides/maintaining-repos.md index 38e4716d821..4f68f899dc1 100644 --- a/docs/src/content/docs/guides/maintaining-repos.md +++ b/docs/src/content/docs/guides/maintaining-repos.md @@ -50,7 +50,7 @@ Review the newly opened issue. Based on the issue content: 5. Otherwise, post a comment thanking the contributor and explaining what information is still needed. ``` -`min-integrity: unapproved` allows repo-assist to see all community content — first-time contributors, external users, everyone. The `safe-outputs` block limits what repo-assist can do in response: it can only apply labels and post comments. Any other GitHub mutation (opening PRs, merging, closing issues) is blocked by the runtime, regardless of what the agent attempts. +`min-integrity: unapproved` allows repo-assist to see content from contributors who have previously interacted with the repository — including first-time contributors and users who have had PRs merged before — while still filtering out content from brand-new GitHub users (`FIRST_TIMER`) and users with no repository association (`NONE`). For most active repositories, this captures the vast majority of community input. The `safe-outputs` block limits what repo-assist can do in response: it can only apply labels and post comments. Any other GitHub mutation (opening PRs, merging, closing issues) is blocked by the runtime, regardless of what the agent attempts. ### Routing to Downstream Agents @@ -134,7 +134,7 @@ When a safe-output validation failure appears in your audit logs, it means the a ## Controlling Workflow Inputs with Integrity Filtering -Integrity filtering is the primary mechanism for controlling what content the agent sees. It evaluates the author of each issue, PR, or comment and removes items that don't meet the configured trust threshold — before the agent's context is assembled. Every public repository automatically applies `min-integrity: approved` as a baseline — repo-assist overrides this to `unapproved` so it can see all incoming issues. +Integrity filtering is the primary mechanism for controlling what content the agent sees. It evaluates the author of each issue, PR, or comment and removes items that don't meet the configured trust threshold — before the agent's context is assembled. Every public repository automatically applies `min-integrity: approved` as a baseline — repo-assist overrides this to `unapproved` so it can see issues from contributors and first-time contributors, not just trusted members. The four configurable levels, from most to least restrictive: @@ -147,7 +147,7 @@ The four configurable levels, from most to least restrictive: Choose based on what the workflow does: -- **Repo-assist / triage workflows**: `unapproved` — classify all community content without acting on it. +- **Repo-assist / triage workflows**: `unapproved` — classify content from contributors and first-time contributors without acting on it. - **Code-modifying workflows** (open PRs, apply patches, close issues): `approved` or `merged` — only act on trusted input. - **Spam detection or analytics**: `none` — see everything, but produce no direct GitHub mutations. @@ -202,30 +202,27 @@ The runtime automatically merges per-workflow values with the variable. Set thes ### Reactions as Trust Signals -Starting from MCPG v0.2.18, maintainers can use GitHub reactions (👍, ❤️) to promote content past the integrity filter without modifying labels. This is useful in repo-assist workflows where a maintainer wants to fast-track an external contribution: +Starting from gh-aw v0.68.2, maintainers can use GitHub reactions (👍, ❤️) to promote content past the integrity filter without modifying labels. This is useful in repo-assist workflows where a maintainer wants to fast-track an external contribution. + +To enable reactions, add the `integrity-reactions` feature flag: ```aw wrap features: integrity-reactions: true -mcp-gateway: - version: "v0.2.18" tools: github: min-integrity: approved - endorsement-reactions: - - "THUMBS_UP" - - "HEART" - disapproval-reactions: - - "THUMBS_DOWN" - - "CONFUSED" - endorser-min-integrity: approved - disapproval-integrity: none ``` -When a trusted member (at or above `endorser-min-integrity`) adds an endorsement reaction to an issue or comment, the item's integrity is promoted to `approved`. A disapproval reaction demotes it to the level set by `disapproval-integrity`. +The compiler handles the rest — when `integrity-reactions: true` is set, it automatically: + +- Enables the CLI proxy (`cli-proxy: true`), which is required for reaction-based integrity decisions +- Injects default endorsement reactions: `THUMBS_UP`, `HEART` +- Injects default disapproval reactions: `THUMBS_DOWN`, `CONFUSED` +- Uses `endorser-min-integrity: approved` (only reactions from owners, members, and collaborators count) +- Uses `disapproval-integrity: none` (a disapproval reaction demotes content to `none`) -> [!IMPORTANT] -> Reactions only work when running through the MCPG proxy mode. They are not available in gateway mode. +These defaults mean that when a trusted member (owner, member, or collaborator) adds a 👍 or ❤️ reaction to an issue or comment, the item's integrity is promoted to `approved` — making it visible to agents using `min-integrity: approved`. Conversely, a 👎 or 😕 reaction from a trusted member demotes the item to `none`. See the [Integrity Filtering Reference](/gh-aw/reference/integrity/) for complete configuration details. diff --git a/pkg/workflow/compiler_difc_proxy.go b/pkg/workflow/compiler_difc_proxy.go index a8707d29cae..1a1ad408d6f 100644 --- a/pkg/workflow/compiler_difc_proxy.go +++ b/pkg/workflow/compiler_difc_proxy.go @@ -334,13 +334,22 @@ func (c *Compiler) generateStopDIFCProxyStep(yaml *strings.Builder, data *Workfl // isCliProxyNeeded returns true if the CLI proxy should be started on the host. // // The CLI proxy is needed when: -// 1. The cli-proxy feature flag is enabled, and +// 1. The cli-proxy feature flag is enabled (explicitly or implicitly), and // 2. The AWF sandbox (firewall) is enabled, and // 3. The AWF version supports CLI proxy flags +// +// The cli-proxy feature is implicitly enabled when integrity-reactions is enabled, +// because reaction-based integrity decisions require the proxy to identify reaction authors. func isCliProxyNeeded(data *WorkflowData) bool { - if !isFeatureEnabled(constants.CliProxyFeatureFlag, data) { + cliProxyEnabled := isFeatureEnabled(constants.CliProxyFeatureFlag, data) + integrityReactionsEnabled := isFeatureEnabled(constants.IntegrityReactionsFeatureFlag, data) + + if !cliProxyEnabled && !integrityReactionsEnabled { return false } + if integrityReactionsEnabled && !cliProxyEnabled { + difcProxyLog.Print("integrity-reactions enabled: implicitly enabling CLI proxy") + } if !isFirewallEnabled(data) { return false } diff --git a/pkg/workflow/compiler_difc_proxy_test.go b/pkg/workflow/compiler_difc_proxy_test.go index 006c8e022e7..3e4f289c1ff 100644 --- a/pkg/workflow/compiler_difc_proxy_test.go +++ b/pkg/workflow/compiler_difc_proxy_test.go @@ -810,3 +810,103 @@ func TestResolveProxyContainerImage(t *testing.T) { }) } } + +// TestIsCliProxyNeeded_IntegrityReactionsImplicitEnable verifies that the CLI proxy +// is implicitly enabled when the integrity-reactions feature flag is set, even without +// an explicit cli-proxy feature flag. +func TestIsCliProxyNeeded_IntegrityReactionsImplicitEnable(t *testing.T) { + awfVersion := "0.25.20" + + tests := []struct { + name string + data *WorkflowData + expected bool + desc string + }{ + { + name: "integrity-reactions enables cli proxy implicitly", + data: &WorkflowData{ + NetworkPermissions: &NetworkPermissions{ + Firewall: &FirewallConfig{ + Enabled: true, + Version: awfVersion, + }, + }, + Features: map[string]any{"integrity-reactions": true}, + }, + expected: true, + desc: "integrity-reactions should implicitly enable the CLI proxy", + }, + { + name: "explicit cli-proxy still works", + data: &WorkflowData{ + NetworkPermissions: &NetworkPermissions{ + Firewall: &FirewallConfig{ + Enabled: true, + Version: awfVersion, + }, + }, + Features: map[string]any{"cli-proxy": true}, + }, + expected: true, + desc: "explicit cli-proxy feature flag should still enable the CLI proxy", + }, + { + name: "both flags enabled", + data: &WorkflowData{ + NetworkPermissions: &NetworkPermissions{ + Firewall: &FirewallConfig{ + Enabled: true, + Version: awfVersion, + }, + }, + Features: map[string]any{"cli-proxy": true, "integrity-reactions": true}, + }, + expected: true, + desc: "both flags together should enable the CLI proxy", + }, + { + name: "neither flag set", + data: &WorkflowData{ + NetworkPermissions: &NetworkPermissions{ + Firewall: &FirewallConfig{ + Enabled: true, + Version: awfVersion, + }, + }, + Features: map[string]any{}, + }, + expected: false, + desc: "no feature flags should not enable the CLI proxy", + }, + { + name: "integrity-reactions without firewall", + data: &WorkflowData{ + Features: map[string]any{"integrity-reactions": true}, + }, + expected: false, + desc: "integrity-reactions without firewall should not enable the CLI proxy", + }, + { + name: "integrity-reactions with old AWF version", + data: &WorkflowData{ + NetworkPermissions: &NetworkPermissions{ + Firewall: &FirewallConfig{ + Enabled: true, + Version: "v0.25.16", + }, + }, + Features: map[string]any{"integrity-reactions": true}, + }, + expected: false, + desc: "integrity-reactions with old AWF version should not enable the CLI proxy", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := isCliProxyNeeded(tt.data) + assert.Equal(t, tt.expected, got, tt.desc) + }) + } +} diff --git a/pkg/workflow/docker.go b/pkg/workflow/docker.go index 6dbe2b8efb1..25588370afb 100644 --- a/pkg/workflow/docker.go +++ b/pkg/workflow/docker.go @@ -103,10 +103,13 @@ func collectDockerImages(tools map[string]any, workflowData *WorkflowData, actio } } - // Add cli-proxy sidecar container when the cli-proxy feature flag is enabled + // Add cli-proxy sidecar container when the cli-proxy is needed (explicitly via + // cli-proxy feature flag, or implicitly via integrity-reactions feature flag) // and the AWF version supports it. Without this, --skip-pull causes AWF to fail // because the cli-proxy image was never pulled. - if isFeatureEnabled(constants.CliProxyFeatureFlag, workflowData) && awfSupportsCliProxy(firewallConfig) { + cliProxyNeeded := isFeatureEnabled(constants.CliProxyFeatureFlag, workflowData) || + isFeatureEnabled(constants.IntegrityReactionsFeatureFlag, workflowData) + if cliProxyNeeded && awfSupportsCliProxy(firewallConfig) { cliProxyImage := constants.DefaultFirewallRegistry + "/cli-proxy:" + awfImageTag if !imageSet[cliProxyImage] { images = append(images, cliProxyImage)