diff --git a/actions/setup/js/create_pull_request_helpers.test.cjs b/actions/setup/js/create_pull_request_helpers.test.cjs
index 2a4ca3ea355..186964ecad9 100644
--- a/actions/setup/js/create_pull_request_helpers.test.cjs
+++ b/actions/setup/js/create_pull_request_helpers.test.cjs
@@ -618,6 +618,38 @@ describe("buildPatchPreviewBlocks", () => {
// buildFallbackIssueBlocks
// ---------------------------------------------------------------------------
describe("buildFallbackIssueBlocks", () => {
+ it.each([
+ ["manifest_protection_create_pr_fallback.md", "create-pull-request", "fallback-to-issue"],
+ ["manifest_protection_push_failed_fallback.md", "create-pull-request", "fallback-to-issue"],
+ ["manifest_protection_push_to_pr_fallback.md", "push-to-pull-request-branch", "fallback-to-issue"],
+ ["manifest_protection_request_review.md", "create-pull-request", "request-review"],
+ ["manifest_protection_request_changes_review.md", "create-pull-request", "request-review"],
+ ])("includes scoped exclusion guidance in %s", (template, safeOutput, policy) => {
+ const children = buildFallbackIssueBlocks(require.resolve(`../md/${template}`), {
+ files: { type: "inline_slot", children: [{ type: "code", value: ".changeset/release.md", agentTainted: true }] },
+ main_body: [],
+ footer: [],
+ create_pr_url: "https://github.com/my-org/my-repo/compare/main...feature?expand=1",
+ pull_number: "42",
+ pr_url: "https://github.com/my-org/my-repo/pull/42",
+ run_url: "https://github.com/my-org/my-repo/actions/runs/123",
+ apply_instructions: "git am changes.patch",
+ branch_name: "feature",
+ title: "Protected file changes",
+ base_branch: "main",
+ repo: "my-org/my-repo",
+ });
+ const yaml = `safe-outputs:\n ${safeOutput}:\n protected-files:\n policy: ${policy}\n exclude:\n - .changeset/`;
+ const guidance = children.flatMap(block => (block.type === "details" ? block.children : [block]));
+ expect(guidance).toContainEqual({ type: "code_block", language: "yaml", value: yaml, agentTainted: false });
+ const { body } = renderReport({ type: "report", title: "Protected files", children });
+ expect(body).toContain("`.changeset/*`");
+ expect(body).toContain("not glob patterns");
+ expect(body).toContain("`allowed-files`");
+ expect(body).toContain("`gh aw compile`");
+ expect(body).not.toContain("protected-files: allowed");
+ });
+
it("composes protected-files fallback content as typed blocks with a compare link", () => {
const mainBody = { type: "paragraph", children: [{ type: "text", value: "Original PR description", agentTainted: true }], agentTainted: true };
const footer = { type: "paragraph", children: [{ type: "text", value: "Workflow footer", agentTainted: false }], agentTainted: false };
diff --git a/actions/setup/js/handle_agent_failure.cjs b/actions/setup/js/handle_agent_failure.cjs
index 13678fa2bf8..f8fef8f3534 100644
--- a/actions/setup/js/handle_agent_failure.cjs
+++ b/actions/setup/js/handle_agent_failure.cjs
@@ -1110,11 +1110,39 @@ function buildCodePushFailureContext(codePushFailureErrors, pullRequest = null,
};
const blockedTypes = [...new Set(manifestErrors.map(e => e.type))];
let yamlSnippet = "safe-outputs:\n";
+ let excludeSnippet = "safe-outputs:\n";
for (const type of blockedTypes) {
const yamlKey = typeToYamlKey[type] || type.replace(/_/g, "-");
yamlSnippet += ` ${yamlKey}:\n protected-files: fallback-to-issue\n`;
+ excludeSnippet += ` ${yamlKey}:\n protected-files:\n policy: blocked\n exclude:\n - .changeset/\n`;
}
context.push(nodes.details([nodes.text("⚙️ Configure "), nodes.code("protected-files: fallback-to-issue")], [nodes.codeBlock(yamlSnippet.trimEnd(), "yaml")]));
+ context.push(
+ nodes.details(
+ [nodes.text("Allow intentional protected-file changes")],
+ [
+ nodes.paragraph([
+ nodes.text("If this workflow is intended to modify these files, add specific filenames or folder prefixes to "),
+ nodes.code("protected-files.exclude"),
+ nodes.text(" under the affected safe output in the workflow Markdown frontmatter. For example, to allow "),
+ nodes.code(".changeset/*"),
+ nodes.text(", exclude the folder using "),
+ nodes.code(".changeset/"),
+ nodes.text(":"),
+ ]),
+ nodes.codeBlock(excludeSnippet.trimEnd(), "yaml"),
+ nodes.paragraph([
+ nodes.text(
+ "Keep your existing policy and exclusions. Folder exclusions use path prefixes, not glob patterns, and allow all files beneath that folder, including subfolders. Other protected files still follow the configured policy. If "
+ ),
+ nodes.code("allowed-files"),
+ nodes.text(" is configured, these paths must also match it. Run "),
+ nodes.code("gh aw compile"),
+ nodes.text(" after updating the workflow."),
+ ]),
+ ]
+ )
+ );
}
// Patch size exceeded section
diff --git a/actions/setup/js/handle_agent_failure.test.cjs b/actions/setup/js/handle_agent_failure.test.cjs
index 290bd2daf7e..19ebea52de8 100644
--- a/actions/setup/js/handle_agent_failure.test.cjs
+++ b/actions/setup/js/handle_agent_failure.test.cjs
@@ -3440,6 +3440,13 @@ describe("handle_agent_failure", () => {
expect(result).toContain("🛡️ Protected Files");
expect(result).toContain("package.json");
expect(result).toContain("protected-files: fallback-to-issue");
+ expect(result).toContain("Allow intentional protected-file changes");
+ expect(result).toContain("`.changeset/*`");
+ expect(result).toContain("policy: blocked");
+ expect(result).toContain("exclude:\n - .changeset/");
+ expect(result).toContain("not glob patterns");
+ expect(result).toContain("`allowed-files`");
+ expect(result).toContain("`gh aw compile`");
// Should NOT contain generic "Code Push Failed" for pure manifest errors
expect(result).not.toContain("Code Push Failed");
});
@@ -3554,6 +3561,7 @@ describe("handle_agent_failure", () => {
const result = buildCodePushFailureContext(errors);
expect(result).toContain("push-to-pull-request-branch:");
expect(result).toContain("protected-files: fallback-to-issue");
+ expect(result).toContain("push-to-pull-request-branch:\n protected-files:\n policy: blocked\n exclude:\n - .changeset/");
expect(result).not.toContain("create-pull-request:");
});
diff --git a/actions/setup/js/testdata/messages_preview.md b/actions/setup/js/testdata/messages_preview.md
index ed77cdac80f..16111382df7 100644
--- a/actions/setup/js/testdata/messages_preview.md
+++ b/actions/setup/js/testdata/messages_preview.md
@@ -1201,6 +1201,24 @@ This is a synthetic body for a test. No GitHub item is created.
To route changes like this to a review issue instead of blocking, configure `protected-files: fallback-to-issue` in your workflow configuration.
+
+Allow intentional protected-file changes
+
+If this workflow is intended to modify these files, add specific filenames or folder prefixes to `safe-outputs.create-pull-request.protected-files.exclude` in the workflow Markdown frontmatter. For example, to allow `.changeset/*`, exclude the folder using `.changeset/`:
+
+```yaml
+safe-outputs:
+ create-pull-request:
+ protected-files:
+ policy: fallback-to-issue
+ exclude:
+ - .changeset/
+```
+
+Keep your existing policy and exclusions. Folder exclusions use path prefixes, not glob patterns, and allow all files beneath that folder, including subfolders. Other protected files still follow the configured policy. If `allowed-files` is configured, these paths must also match it. Run `gh aw compile` after updating the workflow.
+
+
+
> Generated by [Synthetic preview workflow](https://example.test/synthetic/repository/actions/runs/1000)
---
@@ -1238,6 +1256,24 @@ gh pr create --title 'Synthetic pull request' --base main --head synthetic-branc
+
+Allow intentional protected-file changes
+
+If this workflow is intended to modify these files, add a supported exclusion to `safe-outputs.create-pull-request.protected-files.exclude` in the workflow Markdown frontmatter. For example, to allow `.changeset/*`, exclude the top-level dot-folder using `.changeset/`:
+
+```yaml
+safe-outputs:
+ create-pull-request:
+ protected-files:
+ policy: fallback-to-issue
+ exclude:
+ - .changeset/
+```
+
+Keep your existing policy and exclusions. Top-level dot-folder exclusions use folder names, not glob patterns; `.changeset/` allows all files beneath the top-level `.changeset` folder, including subfolders. Other protected files still follow the configured policy. If `allowed-files` is configured, these paths must also match it. Run `gh aw compile` after updating the workflow.
+
+
+
> Generated by [Synthetic preview workflow](https://example.test/synthetic/repository/actions/runs/1000)
---
@@ -1277,6 +1313,24 @@ synthetic_apply_instructions
To route changes like this to a review issue instead of blocking, configure `protected-files: fallback-to-issue` in your workflow configuration.
+
+Allow intentional protected-file changes
+
+If this workflow is intended to modify these files, add specific filenames or folder prefixes to `safe-outputs.push-to-pull-request-branch.protected-files.exclude` in the workflow Markdown frontmatter. For example, to allow `.changeset/*`, exclude the folder using `.changeset/`:
+
+```yaml
+safe-outputs:
+ push-to-pull-request-branch:
+ protected-files:
+ policy: fallback-to-issue
+ exclude:
+ - .changeset/
+```
+
+Keep your existing policy and exclusions. Folder exclusions use path prefixes, not glob patterns, and allow all files beneath that folder, including subfolders. Other protected files still follow the configured policy. If `allowed-files` is configured, these paths must also match it. Run `gh aw compile` after updating the workflow.
+
+
+
---
## Template: manifest_protection_request_changes_review.md
@@ -1287,6 +1341,19 @@ Please verify that each protected-file change is intentional, policy-compliant,
- Protected files: `synthetic/file.txt`
+To allow intentional changes to specific files or folders in future runs, configure `safe-outputs.create-pull-request.protected-files.exclude` in the workflow Markdown frontmatter. For example, this allows `.changeset/*` by excluding the folder:
+
+```yaml
+safe-outputs:
+ create-pull-request:
+ protected-files:
+ policy: request-review
+ exclude:
+ - .changeset/
+```
+
+Keep your existing policy and exclusions. Use folder prefixes, not glob patterns; `.changeset/` also allows subfolders. Other protected files still require review, and `allowed-files`, if configured, must also permit these paths. Run `gh aw compile` after updating the workflow. This configuration change applies to future runs; review this pull request separately.
+
---
## Template: manifest_protection_request_review.md
@@ -1297,6 +1364,24 @@ Please verify that each protected-file change is intentional, policy-compliant,
>
> Protected files: `synthetic/file.txt`
+
+Allow intentional protected-file changes
+
+If this workflow is intended to modify these files, add specific filenames or folder prefixes to `safe-outputs.create-pull-request.protected-files.exclude` in the workflow Markdown frontmatter. For example, to allow `.changeset/*`, exclude the folder using `.changeset/`:
+
+```yaml
+safe-outputs:
+ create-pull-request:
+ protected-files:
+ policy: request-review
+ exclude:
+ - .changeset/
+```
+
+Keep your existing policy and exclusions. Folder exclusions use path prefixes, not glob patterns, and allow all files beneath that folder, including subfolders. Other protected files still require review. If `allowed-files` is configured, these paths must also match it. Run `gh aw compile` after updating the workflow.
+
+
+
---
## Template: max_cache_misses_exceeded.md
diff --git a/actions/setup/md/manifest_protection_create_pr_fallback.md b/actions/setup/md/manifest_protection_create_pr_fallback.md
index 335bcc6e739..f0b5d8573d8 100644
--- a/actions/setup/md/manifest_protection_create_pr_fallback.md
+++ b/actions/setup/md/manifest_protection_create_pr_fallback.md
@@ -27,4 +27,22 @@
To route changes like this to a review issue instead of blocking, configure `protected-files: fallback-to-issue` in your workflow configuration.
+
+Allow intentional protected-file changes
+
+If this workflow is intended to modify these files, add specific filenames or folder prefixes to `safe-outputs.create-pull-request.protected-files.exclude` in the workflow Markdown frontmatter. For example, to allow `.changeset/*`, exclude the folder using `.changeset/`:
+
+```yaml
+safe-outputs:
+ create-pull-request:
+ protected-files:
+ policy: fallback-to-issue
+ exclude:
+ - .changeset/
+```
+
+Keep your existing policy and exclusions. Folder exclusions use path prefixes, not glob patterns, and allow all files beneath that folder, including subfolders. Other protected files still follow the configured policy. If `allowed-files` is configured, these paths must also match it. Run `gh aw compile` after updating the workflow.
+
+
+
{footer}
diff --git a/actions/setup/md/manifest_protection_push_failed_fallback.md b/actions/setup/md/manifest_protection_push_failed_fallback.md
index 6827e726f69..209be613e37 100644
--- a/actions/setup/md/manifest_protection_push_failed_fallback.md
+++ b/actions/setup/md/manifest_protection_push_failed_fallback.md
@@ -29,4 +29,22 @@ gh pr create --title '{title}' --base {base_branch} --head {branch_name} --repo
+
+Allow intentional protected-file changes
+
+If this workflow is intended to modify these files, add a supported exclusion to `safe-outputs.create-pull-request.protected-files.exclude` in the workflow Markdown frontmatter. For example, to allow `.changeset/*`, exclude the top-level dot-folder using `.changeset/`:
+
+```yaml
+safe-outputs:
+ create-pull-request:
+ protected-files:
+ policy: fallback-to-issue
+ exclude:
+ - .changeset/
+```
+
+Keep your existing policy and exclusions. Top-level dot-folder exclusions use folder names, not glob patterns; `.changeset/` allows all files beneath the top-level `.changeset` folder, including subfolders. Other protected files still follow the configured policy. If `allowed-files` is configured, these paths must also match it. Run `gh aw compile` after updating the workflow.
+
+
+
{footer}
diff --git a/actions/setup/md/manifest_protection_push_to_pr_fallback.md b/actions/setup/md/manifest_protection_push_to_pr_fallback.md
index 0120139ca90..9d9bd4d8b2a 100644
--- a/actions/setup/md/manifest_protection_push_to_pr_fallback.md
+++ b/actions/setup/md/manifest_protection_push_to_pr_fallback.md
@@ -30,3 +30,21 @@ The changes are available in the workflow run artifacts:
To route changes like this to a review issue instead of blocking, configure `protected-files: fallback-to-issue` in your workflow configuration.
+
+
+Allow intentional protected-file changes
+
+If this workflow is intended to modify these files, add specific filenames or folder prefixes to `safe-outputs.push-to-pull-request-branch.protected-files.exclude` in the workflow Markdown frontmatter. For example, to allow `.changeset/*`, exclude the folder using `.changeset/`:
+
+```yaml
+safe-outputs:
+ push-to-pull-request-branch:
+ protected-files:
+ policy: fallback-to-issue
+ exclude:
+ - .changeset/
+```
+
+Keep your existing policy and exclusions. Folder exclusions use path prefixes, not glob patterns, and allow all files beneath that folder, including subfolders. Other protected files still follow the configured policy. If `allowed-files` is configured, these paths must also match it. Run `gh aw compile` after updating the workflow.
+
+
diff --git a/actions/setup/md/manifest_protection_request_changes_review.md b/actions/setup/md/manifest_protection_request_changes_review.md
index 4e329c8e8cd..0fd72008949 100644
--- a/actions/setup/md/manifest_protection_request_changes_review.md
+++ b/actions/setup/md/manifest_protection_request_changes_review.md
@@ -3,3 +3,16 @@ Protected files were modified in this pull request and require manual scrutiny b
Please verify that each protected-file change is intentional, policy-compliant, and safe:
- Protected files: {files}
+
+To allow intentional changes to specific files or folders in future runs, configure `safe-outputs.create-pull-request.protected-files.exclude` in the workflow Markdown frontmatter. For example, this allows `.changeset/*` by excluding the folder:
+
+```yaml
+safe-outputs:
+ create-pull-request:
+ protected-files:
+ policy: request-review
+ exclude:
+ - .changeset/
+```
+
+Keep your existing policy and exclusions. Use folder prefixes, not glob patterns; `.changeset/` also allows subfolders. Other protected files still require review, and `allowed-files`, if configured, must also permit these paths. Run `gh aw compile` after updating the workflow. This configuration change applies to future runs; review this pull request separately.
diff --git a/actions/setup/md/manifest_protection_request_review.md b/actions/setup/md/manifest_protection_request_review.md
index 0fd1362a09e..1fefe47b509 100644
--- a/actions/setup/md/manifest_protection_request_review.md
+++ b/actions/setup/md/manifest_protection_request_review.md
@@ -3,3 +3,21 @@
> This pull request is in `request-review` mode and requires explicit human scrutiny before merge.
>
> Protected files: {files}
+
+
+Allow intentional protected-file changes
+
+If this workflow is intended to modify these files, add specific filenames or folder prefixes to `safe-outputs.create-pull-request.protected-files.exclude` in the workflow Markdown frontmatter. For example, to allow `.changeset/*`, exclude the folder using `.changeset/`:
+
+```yaml
+safe-outputs:
+ create-pull-request:
+ protected-files:
+ policy: request-review
+ exclude:
+ - .changeset/
+```
+
+Keep your existing policy and exclusions. Folder exclusions use path prefixes, not glob patterns, and allow all files beneath that folder, including subfolders. Other protected files still require review. If `allowed-files` is configured, these paths must also match it. Run `gh aw compile` after updating the workflow.
+
+