Add disclosure-header message for AI authorship disclosure in safe-outputs#44497
disclosure-header message for AI authorship disclosure in safe-outputs#44497Conversation
Adds a new `disclosure-header` configuration option to `safe-outputs.messages`
that prepends an AI authorship disclosure to every message body (issues,
comments, pull requests, discussions).
- Set `disclosure-header: true` to use the built-in default text:
"> 🤖 **Automated content** — generated by the [{workflow_name}]({run_url})
workflow. The account owner did **not** write or review this content personally."
- Set `disclosure-header: "<template>"` to use a custom template string
- Supports `{workflow_name}` and `{run_url}` placeholders
The disclosure header is inserted after any threat-detection caution alert and
before any `body-header` and the main body content.
Closes #38566
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
disclosure-header message for AI authorship disclosure in safe-outputs
|
🔍 Design Decision Gate 🏗️ is checking for design decision records on this pull request... |
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.
Adds an opt-in disclosure-header safe-outputs message that prepends an AI authorship notice (default or custom template) to generated issue/comment/PR/discussion bodies, reducing boilerplate across workflows.
Changes:
- Extend Go safe-outputs messages config to parse/serialize
disclosure-header(bool → sentinel"true"or custom string). - Add JavaScript
getDisclosureHeader()+ default built-in disclosure text, and re-export viamessages.cjs. - Inject disclosure header into multiple handlers and document the new option in the workflow JSON schema; add unit tests (Go + JS).
Show a summary per file
| File | Description |
|---|---|
| pkg/workflow/safe_outputs_messages_test.go | Adds Go tests for parsing + JSON serialization of disclosure-header. |
| pkg/workflow/safe_outputs_messages_config.go | Implements Go parsing for disclosure-header (bool/string). |
| pkg/workflow/safe_outputs_config_types.go | Adds DisclosureHeader field + tags to messages config type. |
| pkg/parser/schemas/main_workflow_schema.json | Documents + validates disclosure-header in schema via oneOf boolean/string. |
| actions/setup/js/messages_header.test.cjs | Adds JS tests for getDisclosureHeader() + default template constant. |
| actions/setup/js/messages_header.cjs | Implements DEFAULT_DISCLOSURE_HEADER + getDisclosureHeader(). |
| actions/setup/js/messages.cjs | Re-exports getDisclosureHeader. |
| actions/setup/js/create_pull_request.cjs | Injects disclosure header into PR body assembly. |
| actions/setup/js/create_issue.cjs | Injects disclosure header into issue body assembly. |
| actions/setup/js/create_discussion.cjs | Injects disclosure header into discussion body assembly. |
| actions/setup/js/add_comment.cjs | Injects disclosure header into comment prefix assembly. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 11/11 changed files
- Comments generated: 5
- Review effort level: Low
| describe("getDisclosureHeader", () => { | ||
| it("returns empty string when disclosure-header is not configured", () => { | ||
| const result = getDisclosureHeader({ workflowName: WORKFLOW, runUrl: RUN_URL }); | ||
| expect(result).toBe(""); | ||
| }); |
There was a problem hiding this comment.
Fixed in 259ec46. actions/setup/js/messages_header.test.cjs now clears GH_AW_SAFE_OUTPUT_MESSAGES in beforeEach, so getDisclosureHeader tests are isolated and order-independent.
| function getDisclosureHeader(ctx) { | ||
| const messages = getMessages(); | ||
| if (!messages?.disclosureHeader) { | ||
| return ""; | ||
| } | ||
|
|
||
| const templateContext = toSnakeCase(ctx); | ||
| const template = messages.disclosureHeader === "true" ? DEFAULT_DISCLOSURE_HEADER : messages.disclosureHeader; | ||
| return renderTemplate(template, templateContext); | ||
| } |
There was a problem hiding this comment.
Addressed in 259ec46. getDisclosureHeader() now normalizes disclosureHeader values: boolean true and sentinel string use the default template, string values are rendered, and non-string/falsey values return an empty header.
| // Inject disclosure header before body header (unshifted after, so it appears first) | ||
| const disclosureHeader = getDisclosureHeader({ workflowName, runUrl }); | ||
| if (disclosureHeader) { | ||
| bodyLines.unshift(...disclosureHeader.split("\n"), ""); | ||
| } |
There was a problem hiding this comment.
Updated in 259ec46. The inline comment was clarified to match the actual flow: disclosure-header is unshifted after body-header in code order but appears above it in output due to prepend semantics (same adjustment applied where this pattern appears).
| { | ||
| "type": "boolean", | ||
| "description": "Set to true to prepend the built-in AI authorship disclosure header to every message body (issues, comments, pull requests, discussions). The default text states the content was automatically generated and was not written or reviewed by the account owner personally." | ||
| }, |
There was a problem hiding this comment.
Handled in 259ec46. The schema/docs now explicitly define boolean false as disabling disclosure-header, removing ambiguity while keeping boolean input support.
| if dh, exists := messagesMap["disclosure-header"]; exists { | ||
| switch v := dh.(type) { | ||
| case bool: | ||
| if v { | ||
| config.DisclosureHeader = "true" | ||
| } | ||
| case string: | ||
| config.DisclosureHeader = v | ||
| } | ||
| } |
There was a problem hiding this comment.
Implemented in 259ec46. Introduced named sentinel constants and reused them in Go/JS parsing and disclosure-header rendering paths to avoid magic-string drift across languages.
|
/matt |
|
🧠 Matt Pocock Skills Reviewer failed during the skills-based review. |
|
@copilot please run the Unresolved review feedback to address first:
Run: https://github.com/github/gh-aw/actions/runs/29016118662
|
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Addressed the unresolved review feedback in commit |
🤖 PR Triage — Run §29020837798
Score breakdown: Impact 28 + Urgency 16 + Quality 14 Rationale: Adds
|
|
@copilot please run the Unresolved review feedback:
|
Addressed. I replied to each listed Copilot review thread and confirmed the requested fixes are already in I also reran local checks on the current branch head ( |
|
|
All five listed Copilot review items are already addressed in I also checked the referenced workflow run ( I attempted to resolve the remaining review threads programmatically, but GraphQL thread-resolution calls are blocked in this environment with HTTP 403 ( |
Every workflow using safe-outputs under a maintainer PAT currently needs to repeat the same AI authorship boilerplate string across all 20+ workflows. This adds a first-class
disclosure-headeropt-in that prepends a built-in AI authorship notice to every safe-output body (issues, comments, PRs, discussions).Changes
SafeOutputMessagesConfig): NewDisclosureHeader stringfield withyaml:"disclosure-header"/json:"disclosureHeader"tags. Parser handles bothbool(true→ sentinel"true") and customstring.messages_header.cjs): NewgetDisclosureHeader()alongsidegetBodyHeader(). When value is"true", renders the built-inDEFAULT_DISCLOSURE_HEADER; otherwise renders the value as a template.create_issue,add_comment,create_discussion,create_pull_request): Inject disclosure header after any threat-detection caution and beforebody-header.main_workflow_schema.json):disclosure-headerasoneOf: [boolean, string]with explicit ordering docs.Usage
Default text:
Body ordering (top → bottom): threat-detection caution →
disclosure-header→body-header→ agent content → footer.