From f28e96cbe85c36be7176effaafb77ab938e05493 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 23:28:26 +0000 Subject: [PATCH 1/3] Initial plan From af9a1700080cd092a1346b7e3877e4e86f14f4a1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 23:47:17 +0000 Subject: [PATCH 2/3] feat: add footer:false support to add-comment safe output and FAQ entry - Add Footer field to AddCommentsConfig struct with preprocessing - Pass footer to add_comment handler config builder (respects global footer setting) - Add footer check in add_comment.cjs: conditionally add footer or just XML marker - Add footer field to add-comment JSON schema - Add tests for add-comment footer configuration (per-handler and global inheritance) - Add FAQ entry about suppressing Generated by... text - Add footer tip to add-comment section in safe-outputs.md Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- actions/setup/js/add_comment.cjs | 12 +++- docs/src/content/docs/reference/faq.md | 27 ++++++++ .../content/docs/reference/safe-outputs.md | 4 ++ pkg/parser/schemas/main_workflow_schema.json | 5 ++ pkg/workflow/add_comment.go | 5 ++ pkg/workflow/compiler_safe_outputs_config.go | 1 + pkg/workflow/safe_outputs_footer_test.go | 61 +++++++++++++++++++ 7 files changed, 112 insertions(+), 3 deletions(-) diff --git a/actions/setup/js/add_comment.cjs b/actions/setup/js/add_comment.cjs index 2cf3b0fa463..51843af2f12 100644 --- a/actions/setup/js/add_comment.cjs +++ b/actions/setup/js/add_comment.cjs @@ -5,7 +5,7 @@ * @typedef {import('./types/handler-factory').HandlerFactoryFunction} HandlerFactoryFunction */ -const { generateFooterWithMessages } = require("./messages_footer.cjs"); +const { generateFooterWithMessages, generateXMLMarker } = require("./messages_footer.cjs"); const { getRepositoryUrl } = require("./get_repository_url.cjs"); const { replaceTemporaryIdReferences, loadTemporaryIdMapFromResolved, resolveRepoIssueTarget } = require("./temporary_id.cjs"); const { getTrackerID } = require("./get_tracker_id.cjs"); @@ -302,6 +302,7 @@ async function main(config = {}) { const commentTarget = config.target || "triggering"; const maxCount = config.max || 20; const { defaultTargetRepo, allowedRepos } = resolveTargetRepoConfig(config); + const includeFooter = parseBoolTemplatable(config.footer, true); // Create an authenticated GitHub client. Uses config["github-token"] when set // (for cross-repository operations), otherwise falls back to the step-level github. @@ -524,8 +525,13 @@ async function main(config = {}) { const triggeringPRNumber = context.payload.pull_request?.number; const triggeringDiscussionNumber = context.payload.discussion?.number; - // Use generateFooterWithMessages to respect custom footer configuration - processedBody += generateFooterWithMessages(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, triggeringDiscussionNumber).trimEnd(); + if (includeFooter) { + // When footer is enabled, add full footer with attribution and XML markers + processedBody += generateFooterWithMessages(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, triggeringDiscussionNumber).trimEnd(); + } else { + // When footer is disabled, only add XML marker for searchability (no visible attribution text) + processedBody += "\n\n" + generateXMLMarker(workflowName, runUrl); + } // Enforce max limits again after adding footer and metadata // This ensures the final body (including generated content) doesn't exceed limits diff --git a/docs/src/content/docs/reference/faq.md b/docs/src/content/docs/reference/faq.md index 88d9fc08171..0d9363d8057 100644 --- a/docs/src/content/docs/reference/faq.md +++ b/docs/src/content/docs/reference/faq.md @@ -260,6 +260,33 @@ The easy way to fix this problem is to set a secret `GH_AW_CI_TRIGGER_TOKEN` wit See [Triggering CI](/gh-aw/reference/triggering-ci/) for more details on how to configure workflows to run CI checks on PRs created by agentic workflows. +### How do I suppress the "Generated by..." text in workflow outputs? + +When workflows create or update issues, pull requests, discussions, or post comments, they append a `> Generated by [Workflow Name](run_url) for issue #N` attribution line. Use `footer: false` to hide this visible text while preserving the hidden XML markers used for search and tracking. + +**Hide footers globally** (all safe output types): + +```yaml wrap +safe-outputs: + footer: false + add-comment: + create-issue: + title-prefix: "[ai] " +``` + +**Hide footers for specific output types only:** + +```yaml wrap +safe-outputs: + footer: false # hide for all by default + create-pull-request: + footer: true # override: show footer for PRs only +``` + +Even with `footer: false`, the hidden `` XML marker is still included in the content for searchability - you can search GitHub for `"gh-aw-workflow-id: my-workflow" in:body` to find all items created by a workflow. + +See [Footer Control](/gh-aw/reference/footers/) for complete documentation including per-handler overrides and PR review footer options. + ## Workflow Design ### Should I focus on one workflow, or write many different ones? diff --git a/docs/src/content/docs/reference/safe-outputs.md b/docs/src/content/docs/reference/safe-outputs.md index f13df64e65f..2736267a37c 100644 --- a/docs/src/content/docs/reference/safe-outputs.md +++ b/docs/src/content/docs/reference/safe-outputs.md @@ -236,8 +236,12 @@ safe-outputs: allowed-repos: ["org/repo1", "org/repo2"] # additional allowed repositories hide-older-comments: true # hide previous comments from same workflow allowed-reasons: [outdated] # restrict hiding reasons (optional) + footer: false # omit AI-generated footer (default: true) ``` +> [!TIP] +> Use `footer: false` to suppress the "Generated by..." attribution line in posted comments. See [Footer Control](/gh-aw/reference/footers/) for global and per-handler options. + The author of the parent issue, PR, or discussion receiving the comment is automatically preserved as an allowed mention. This means `@username` references to the issue/PR/discussion author are not neutralized when the workflow posts a reply. #### Hide Older Comments diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 8853c25bd0d..aeb4566a31e 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -5068,6 +5068,11 @@ "github-token": { "$ref": "#/$defs/github_token", "description": "GitHub token to use for this specific output type. Overrides global github-token if specified." + }, + "footer": { + "type": "boolean", + "description": "Controls whether AI-generated footer is added to the comment. When false, the visible footer content is omitted but XML markers (workflow-id, metadata) are still included for searchability. Defaults to true.", + "default": true } }, "additionalProperties": false, diff --git a/pkg/workflow/add_comment.go b/pkg/workflow/add_comment.go index cc8e59194fb..b6efba4ff1f 100644 --- a/pkg/workflow/add_comment.go +++ b/pkg/workflow/add_comment.go @@ -27,6 +27,7 @@ type AddCommentsConfig struct { Issues *bool `yaml:"issues,omitempty"` // When false, excludes issues:write permission and issues from event condition. Default (nil or true) includes issues:write. PullRequests *bool `yaml:"pull-requests,omitempty"` // When false, excludes pull-requests:write permission and PRs from event condition. Default (nil or true) includes pull-requests:write. Discussions *bool `yaml:"discussions,omitempty"` // When false, excludes discussions:write permission and discussions from event condition. Default (nil or true) includes discussions:write. + Footer *string `yaml:"footer,omitempty"` // Controls whether AI-generated footer is added. When false, visible footer is omitted but XML markers are kept. } // buildCreateOutputAddCommentJob creates the add_comment job @@ -162,6 +163,10 @@ func (c *Compiler) parseCommentsConfig(outputMap map[string]any) *AddCommentsCon addCommentLog.Printf("Invalid hide-older-comments value: %v", err) return nil } + if err := preprocessBoolFieldAsString(configData, "footer", addCommentLog); err != nil { + addCommentLog.Printf("Invalid footer value: %v", err) + return nil + } // Pre-process templatable int fields if err := preprocessIntFieldAsString(configData, "max", addCommentLog); err != nil { diff --git a/pkg/workflow/compiler_safe_outputs_config.go b/pkg/workflow/compiler_safe_outputs_config.go index c1c0282d3f9..d336c6468aa 100644 --- a/pkg/workflow/compiler_safe_outputs_config.go +++ b/pkg/workflow/compiler_safe_outputs_config.go @@ -157,6 +157,7 @@ var handlerRegistry = map[string]handlerBuilder{ AddIfNotEmpty("target-repo", c.TargetRepoSlug). AddStringSlice("allowed_repos", c.AllowedRepos). AddIfNotEmpty("github-token", c.GitHubToken). + AddTemplatableBool("footer", getEffectiveFooterForTemplatable(c.Footer, cfg.Footer)). Build() }, "create_discussion": func(cfg *SafeOutputsConfig) map[string]any { diff --git a/pkg/workflow/safe_outputs_footer_test.go b/pkg/workflow/safe_outputs_footer_test.go index aa208e58c61..b4472047120 100644 --- a/pkg/workflow/safe_outputs_footer_test.go +++ b/pkg/workflow/safe_outputs_footer_test.go @@ -26,6 +26,63 @@ func TestFooterConfiguration(t *testing.T) { assert.Equal(t, "false", *config.CreateIssues.Footer) } +func TestAddCommentFooterConfiguration(t *testing.T) { + t.Run("footer: false on add-comment", func(t *testing.T) { + compiler := NewCompiler() + frontmatter := map[string]any{ + "name": "Test", + "safe-outputs": map[string]any{ + "add-comment": map[string]any{"footer": false}, + }, + } + config := compiler.extractSafeOutputsConfig(frontmatter) + require.NotNil(t, config) + require.NotNil(t, config.AddComments) + require.NotNil(t, config.AddComments.Footer) + assert.Equal(t, "false", *config.AddComments.Footer, "add-comment footer should be false") + }) + + t.Run("global footer: false propagates to add-comment", func(t *testing.T) { + compiler := NewCompiler() + frontmatter := map[string]any{ + "name": "Test", + "safe-outputs": map[string]any{ + "footer": false, + "add-comment": map[string]any{}, + }, + } + config := compiler.extractSafeOutputsConfig(frontmatter) + require.NotNil(t, config) + require.NotNil(t, config.Footer) + assert.False(t, *config.Footer, "Global footer should be false") + + workflowData := &WorkflowData{ + Name: "Test", + SafeOutputs: config, + } + var steps []string + compiler.addHandlerManagerConfigEnvVar(&steps, workflowData) + + 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 handlerConfig map[string]any + err := json.Unmarshal([]byte(jsonStr), &handlerConfig) + require.NoError(t, err) + + addCommentConfig, ok := handlerConfig["add_comment"].(map[string]any) + require.True(t, ok, "add_comment handler config should exist") + assert.Equal(t, false, addCommentConfig["footer"], "add_comment should inherit global footer: false") + } + } + } + }) +} + func TestGlobalFooterConfiguration(t *testing.T) { t.Run("global footer: false applies to all handlers", func(t *testing.T) { compiler := NewCompiler() @@ -33,6 +90,7 @@ func TestGlobalFooterConfiguration(t *testing.T) { "name": "Test", "safe-outputs": map[string]any{ "footer": false, // Global footer control + "add-comment": map[string]any{}, "create-issue": map[string]any{"title-prefix": "[test] "}, "create-pull-request": nil, "create-discussion": nil, @@ -69,6 +127,9 @@ func TestGlobalFooterConfiguration(t *testing.T) { require.NoError(t, err) // All handlers should have footer: false from global setting + addCommentConfig, ok := handlerConfig["add_comment"].(map[string]any) + require.True(t, ok, "add_comment handler config should exist in global footer test") + assert.Equal(t, false, addCommentConfig["footer"], "add_comment should inherit global footer: false") if issueConfig, ok := handlerConfig["create_issue"].(map[string]any); ok { assert.Equal(t, false, issueConfig["footer"], "create_issue should inherit global footer: false") } From 21c1fafafc58f7920d883103db7aa8390a587368 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 23:57:36 +0000 Subject: [PATCH 3/3] fix: move footer before github-token in add-comment schema for consistency Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/parser/schemas/main_workflow_schema.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index aeb4566a31e..dee5868af64 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -5065,14 +5065,14 @@ "type": "boolean", "description": "Controls whether the workflow requests pull-requests:write permission for add-comment and includes pull requests in the event trigger condition. Default: true (includes pull-requests:write). Set to false to disable pull request commenting." }, - "github-token": { - "$ref": "#/$defs/github_token", - "description": "GitHub token to use for this specific output type. Overrides global github-token if specified." - }, "footer": { "type": "boolean", "description": "Controls whether AI-generated footer is added to the comment. When false, the visible footer content is omitted but XML markers (workflow-id, metadata) are still included for searchability. Defaults to true.", "default": true + }, + "github-token": { + "$ref": "#/$defs/github_token", + "description": "GitHub token to use for this specific output type. Overrides global github-token if specified." } }, "additionalProperties": false,