From b3565d0645f758a05c74500cd56a327785d89275 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 06:37:02 +0000
Subject: [PATCH 01/17] Initial plan


From ce8b3ef848b71436bfc53e2e2766426d84b1f31f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 07:07:54 +0000
Subject: [PATCH 02/17] feat: add safe-output actions support for mounting
 custom GitHub Actions as MCP tools

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .../setup/js/safe_output_action_handler.cjs   | 102 ++++
 .../setup/js/safe_output_handler_manager.cjs  |  31 +-
 actions/setup/js/safe_output_helpers.cjs      |  32 ++
 pkg/workflow/compiler_safe_outputs_job.go     |  21 +-
 pkg/workflow/compiler_safe_outputs_steps.go   |   7 +
 pkg/workflow/compiler_types.go                |   1 +
 pkg/workflow/safe_outputs_actions.go          | 447 ++++++++++++++++++
 pkg/workflow/safe_outputs_actions_test.go     | 417 ++++++++++++++++
 pkg/workflow/safe_outputs_config.go           |   8 +
 pkg/workflow/safe_outputs_state.go            |  11 +
 pkg/workflow/safe_outputs_tools_filtering.go  |  39 +-
 11 files changed, 1113 insertions(+), 3 deletions(-)
 create mode 100644 actions/setup/js/safe_output_action_handler.cjs
 create mode 100644 pkg/workflow/safe_outputs_actions.go
 create mode 100644 pkg/workflow/safe_outputs_actions_test.go

diff --git a/actions/setup/js/safe_output_action_handler.cjs b/actions/setup/js/safe_output_action_handler.cjs
new file mode 100644
index 00000000000..836d04c0b2c
--- /dev/null
+++ b/actions/setup/js/safe_output_action_handler.cjs
@@ -0,0 +1,102 @@
+// @ts-check
+/// <reference types="@actions/github-script" />
+
+/**
+ * @typedef {import('./types/handler-factory').HandlerFactoryFunction} HandlerFactoryFunction
+ */
+
+const { replaceTemporaryIdReferences } = require("./temporary_id.cjs");
+const { getErrorMessage } = require("./error_helpers.cjs");
+
+/**
+ * Main handler factory for a custom safe output action.
+ *
+ * Each configured safe-output action gets its own instance of this factory function,
+ * invoked with a config that includes `action_name` (the normalized tool name).
+ *
+ * The handler:
+ *  1. Enforces that the action is called at most once (per the spec).
+ *  2. Applies temporary ID substitutions to all string-valued fields in the payload.
+ *  3. Exports the processed payload as a step output named `action_<name>_payload`.
+ *
+ * The compiler generates a corresponding GitHub Actions step with:
+ *   if: steps.process_safe_outputs.outputs.action_<name>_payload != ''
+ *   uses: <resolved-action-ref>
+ *   with:
+ *     <input>: ${{ fromJSON(steps.process_safe_outputs.outputs.action_<name>_payload).<input> }}
+ *
+ * @type {HandlerFactoryFunction}
+ */
+async function main(config = {}) {
+  const actionName = config.action_name || "unknown_action";
+  const outputKey = `action_${actionName}_payload`;
+
+  core.info(`Custom action handler initialized: action_name=${actionName}, output_key=${outputKey}`);
+
+  // Track whether this action has been called (enforces once-only constraint)
+  let called = false;
+
+  /**
+   * Handler function that processes a single tool call for this action.
+   * Applies temporary ID substitutions and exports the payload as a step output.
+   *
+   * @param {Object} message - The tool call message from the agent output
+   * @param {Object} resolvedTemporaryIds - Map of temp IDs to resolved values (plain object)
+   * @param {Map<string, Object>} temporaryIdMap - Live map of temporary IDs (for substitution)
+   * @returns {Promise<Object>} Result with success/error status
+   */
+  return async function handleCustomAction(message, resolvedTemporaryIds, temporaryIdMap) {
+    // Enforce once-only constraint
+    if (called) {
+      const error = `Action "${actionName}" can only be called once per workflow run`;
+      core.warning(error);
+      return {
+        success: false,
+        error,
+      };
+    }
+    called = true;
+
+    try {
+      core.info(`Processing custom action: ${actionName}`);
+
+      // Build the processed payload by applying temporary ID substitutions to all
+      // string-valued fields. Non-string fields (numbers, booleans, objects, arrays)
+      // are passed through as-is.
+      const processedInputs = {};
+      for (const [key, value] of Object.entries(message)) {
+        // Skip internal fields that are not action inputs
+        if (key === "type") {
+          continue;
+        }
+
+        if (typeof value === "string") {
+          // Apply temporary ID reference substitution (e.g., "aw_abc1" → "42")
+          processedInputs[key] = replaceTemporaryIdReferences(value, temporaryIdMap);
+        } else {
+          processedInputs[key] = value;
+        }
+      }
+
+      // Export the processed payload as a step output
+      const payloadJSON = JSON.stringify(processedInputs);
+      core.setOutput(outputKey, payloadJSON);
+      core.info(`✓ Custom action "${actionName}": exported payload as "${outputKey}" (${payloadJSON.length} bytes)`);
+
+      return {
+        success: true,
+        action_name: actionName,
+        payload: payloadJSON,
+      };
+    } catch (error) {
+      const errorMessage = getErrorMessage(error);
+      core.error(`Failed to process custom action "${actionName}": ${errorMessage}`);
+      return {
+        success: false,
+        error: `Failed to process custom action "${actionName}": ${errorMessage}`,
+      };
+    }
+  };
+}
+
+module.exports = { main };
diff --git a/actions/setup/js/safe_output_handler_manager.cjs b/actions/setup/js/safe_output_handler_manager.cjs
index b7a4fea5758..e3a696e8145 100644
--- a/actions/setup/js/safe_output_handler_manager.cjs
+++ b/actions/setup/js/safe_output_handler_manager.cjs
@@ -20,7 +20,7 @@ const { getIssuesToAssignCopilot } = require("./create_issue.cjs");
 const { createReviewBuffer } = require("./pr_review_buffer.cjs");
 const { sanitizeContent } = require("./sanitize_content.cjs");
 const { createManifestLogger, ensureManifestExists, extractCreatedItemFromResult } = require("./safe_output_manifest.cjs");
-const { loadCustomSafeOutputJobTypes, loadCustomSafeOutputScriptHandlers } = require("./safe_output_helpers.cjs");
+const { loadCustomSafeOutputJobTypes, loadCustomSafeOutputScriptHandlers, loadCustomSafeOutputActionHandlers } = require("./safe_output_helpers.cjs");
 const { emitSafeOutputActionOutputs } = require("./safe_outputs_action_outputs.cjs");
 
 /**
@@ -194,6 +194,35 @@ async function loadHandlers(config, prReviewBuffer) {
     }
   }
 
+  // Load custom action handlers from GH_AW_SAFE_OUTPUT_ACTIONS
+  // These are GitHub Actions configured in safe-outputs.actions. The handler applies
+  // temporary ID substitutions to the payload and exports `action_<name>_payload` outputs
+  // that compiler-generated `uses:` steps consume.
+  const customActionHandlers = loadCustomSafeOutputActionHandlers();
+  if (customActionHandlers.size > 0) {
+    core.info(`Loading ${customActionHandlers.size} custom action handler(s): ${[...customActionHandlers.keys()].join(", ")}`);
+    const actionHandlerPath = require("path").join(__dirname, "safe_output_action_handler.cjs");
+    for (const [actionType, actionName] of customActionHandlers) {
+      try {
+        const actionModule = require(actionHandlerPath);
+        if (actionModule && typeof actionModule.main === "function") {
+          const handlerConfig = { action_name: actionName, ...(config[actionType] || {}) };
+          const messageHandler = await actionModule.main(handlerConfig);
+          if (typeof messageHandler !== "function") {
+            core.warning(`✗ Custom action handler ${actionType} main() did not return a function (got ${typeof messageHandler}) — this handler will be skipped`);
+          } else {
+            messageHandlers.set(actionType, messageHandler);
+            core.info(`✓ Loaded and initialized custom action handler for: ${actionType}`);
+          }
+        } else {
+          core.warning(`Custom action handler module does not export a main function — skipping ${actionType}`);
+        }
+      } catch (error) {
+        core.warning(`Failed to load custom action handler for ${actionType}: ${getErrorMessage(error)} — this handler will be skipped`);
+      }
+    }
+  }
+
   core.info(`Loaded ${messageHandlers.size} handler(s)`);
   return messageHandlers;
 }
diff --git a/actions/setup/js/safe_output_helpers.cjs b/actions/setup/js/safe_output_helpers.cjs
index 6fe9dc01c39..d178fb75c36 100644
--- a/actions/setup/js/safe_output_helpers.cjs
+++ b/actions/setup/js/safe_output_helpers.cjs
@@ -363,12 +363,44 @@ function loadCustomSafeOutputScriptHandlers() {
   }
 }
 
+/**
+ * Load custom safe output action handlers from environment variable.
+ * These are GitHub Actions configured in safe-outputs.actions that are processed
+ * by compiler-injected `uses:` steps after the handler manager exports their payloads.
+ * The handler manager processes the tool call, applies temporary ID substitutions,
+ * and exports `action_<name>_payload` outputs that the injected steps consume.
+ * @returns {Map<string, string>} Map of normalized action type name to action name (for handler config)
+ */
+function loadCustomSafeOutputActionHandlers() {
+  const safeOutputActionsEnv = process.env.GH_AW_SAFE_OUTPUT_ACTIONS;
+  if (!safeOutputActionsEnv) {
+    return new Map();
+  }
+
+  try {
+    const safeOutputActions = JSON.parse(safeOutputActionsEnv);
+    // The environment variable is a map of normalized action names to themselves
+    const actionHandlers = new Map(Object.entries(safeOutputActions));
+    if (typeof core !== "undefined") {
+      core.debug(`Loaded ${actionHandlers.size} custom safe output action handler(s): ${[...actionHandlers.keys()].join(", ")}`);
+    }
+    return actionHandlers;
+  } catch (error) {
+    if (typeof core !== "undefined") {
+      const { getErrorMessage } = require("./error_helpers.cjs");
+      core.warning(`Failed to parse GH_AW_SAFE_OUTPUT_ACTIONS: ${getErrorMessage(error)}`);
+    }
+    return new Map();
+  }
+}
+
 module.exports = {
   parseAllowedItems,
   parseMaxCount,
   resolveTarget,
   loadCustomSafeOutputJobTypes,
   loadCustomSafeOutputScriptHandlers,
+  loadCustomSafeOutputActionHandlers,
   resolveIssueNumber,
   extractAssignees,
   matchesBlockedPattern,
diff --git a/pkg/workflow/compiler_safe_outputs_job.go b/pkg/workflow/compiler_safe_outputs_job.go
index fa84c71518b..528fe284011 100644
--- a/pkg/workflow/compiler_safe_outputs_job.go
+++ b/pkg/workflow/compiler_safe_outputs_job.go
@@ -149,7 +149,8 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa
 		data.SafeOutputs.AutofixCodeScanningAlert != nil ||
 		data.SafeOutputs.MissingTool != nil ||
 		data.SafeOutputs.MissingData != nil ||
-		len(data.SafeOutputs.Scripts) > 0 // Custom scripts run in the handler loop
+		len(data.SafeOutputs.Scripts) > 0 || // Custom scripts run in the handler loop
+		len(data.SafeOutputs.Actions) > 0 // Custom actions need handler to export their payloads
 
 	// Note: All project-related operations are now handled by the unified handler.
 	// The project handler manager has been removed.
@@ -222,6 +223,24 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa
 		outputs["create_agent_session_session_url"] = "${{ steps.create_agent_session.outputs.session_url }}"
 	}
 
+	// 5. Custom action steps — compiler-generated steps for each configured safe-output action.
+	// These steps run after the handler manager, which processes the agent payload and exports
+	// a JSON payload output for each action tool call. Each step is guarded by an `if:` condition
+	// that checks whether the handler manager exported a payload for this action.
+	if len(data.SafeOutputs.Actions) > 0 {
+		// Resolve action.yml for all actions (fetches inputs/descriptions at compile time)
+		c.resolveAllActions(data, markdownPath)
+
+		actionStepYAML := c.buildActionSteps(data)
+		steps = append(steps, actionStepYAML...)
+
+		// Register each action as having a handler manager output
+		for actionName := range data.SafeOutputs.Actions {
+			normalizedName := stringutil.NormalizeSafeOutputIdentifier(actionName)
+			safeOutputStepNames = append(safeOutputStepNames, "action_"+normalizedName)
+		}
+	}
+
 	// The outputs and permissions are configured in the handler manager section above
 	if data.SafeOutputs.AddReviewer != nil {
 		outputs["add_reviewer_reviewers_added"] = "${{ steps.process_safe_outputs.outputs.reviewers_added }}"
diff --git a/pkg/workflow/compiler_safe_outputs_steps.go b/pkg/workflow/compiler_safe_outputs_steps.go
index 438360066dc..3ebd8727162 100644
--- a/pkg/workflow/compiler_safe_outputs_steps.go
+++ b/pkg/workflow/compiler_safe_outputs_steps.go
@@ -349,6 +349,13 @@ func (c *Compiler) buildHandlerManagerStep(data *WorkflowData) []string {
 		consolidatedSafeOutputsStepsLog.Print("Added GH_AW_SAFE_OUTPUT_SCRIPTS env var for custom script handlers")
 	}
 
+	// Add GH_AW_SAFE_OUTPUT_ACTIONS so the handler manager can load custom action handlers.
+	// The env var maps normalized action names to themselves (reserved for future extensibility).
+	if customActionsJSON := buildCustomSafeOutputActionsJSON(data); customActionsJSON != "" {
+		steps = append(steps, fmt.Sprintf("          GH_AW_SAFE_OUTPUT_ACTIONS: %q\n", customActionsJSON))
+		consolidatedSafeOutputsStepsLog.Print("Added GH_AW_SAFE_OUTPUT_ACTIONS env var for custom action handlers")
+	}
+
 	// Add custom safe output env vars
 	c.addCustomSafeOutputEnvVars(&steps, data)
 
diff --git a/pkg/workflow/compiler_types.go b/pkg/workflow/compiler_types.go
index 71c2b86ee9e..eb3cba6662a 100644
--- a/pkg/workflow/compiler_types.go
+++ b/pkg/workflow/compiler_types.go
@@ -502,6 +502,7 @@ type SafeOutputsConfig struct {
 	IDToken                         *string                                `yaml:"id-token,omitempty"`                     // Override id-token permission: "write" to force-add, "none" to disable auto-detection
 	ConcurrencyGroup                string                                 `yaml:"concurrency-group,omitempty"`            // Concurrency group for the safe-outputs job (cancel-in-progress is always false)
 	Environment                     string                                 `yaml:"environment,omitempty"`                  // Override the GitHub deployment environment for the safe-outputs job (defaults to the top-level environment: field)
+	Actions                         map[string]*SafeOutputActionConfig     `yaml:"actions,omitempty"`                      // Custom GitHub Actions mounted as safe output tools (resolved at compile time)
 	AutoInjectedCreateIssue         bool                                   `yaml:"-"`                                      // Internal: true when create-issues was automatically injected by the compiler (not user-configured)
 }
 
diff --git a/pkg/workflow/safe_outputs_actions.go b/pkg/workflow/safe_outputs_actions.go
new file mode 100644
index 00000000000..ff4d8364251
--- /dev/null
+++ b/pkg/workflow/safe_outputs_actions.go
@@ -0,0 +1,447 @@
+package workflow
+
+import (
+	"context"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+	"time"
+
+	"github.com/github/gh-aw/pkg/logger"
+	"github.com/github/gh-aw/pkg/stringutil"
+	"go.yaml.in/yaml/v3"
+)
+
+var safeOutputActionsLog = logger.New("workflow:safe_outputs_actions")
+
+// SafeOutputActionConfig holds configuration for a single custom safe output action.
+// Each configured action is resolved at compile time to get its inputs from action.yml,
+// and is mounted as an MCP tool that the AI agent can call once per workflow run.
+type SafeOutputActionConfig struct {
+	Uses        string `yaml:"uses"`
+	Description string `yaml:"description,omitempty"` // optional override of the action's description
+
+	// Computed at compile time (not from frontmatter):
+	ResolvedRef       string                      `yaml:"-"` // Pinned action reference (e.g., "owner/repo@sha # v1")
+	Inputs            map[string]*ActionYAMLInput `yaml:"-"` // Inputs parsed from action.yml
+	ActionDescription string                      `yaml:"-"` // Description from action.yml
+}
+
+// ActionYAMLInput holds an input definition parsed from a GitHub Action's action.yml.
+type ActionYAMLInput struct {
+	Description string `yaml:"description,omitempty"`
+	Required    bool   `yaml:"required,omitempty"`
+	Default     string `yaml:"default,omitempty"`
+}
+
+// actionYAMLFile is the parsed structure of a GitHub Action's action.yml.
+type actionYAMLFile struct {
+	Name        string                      `yaml:"name"`
+	Description string                      `yaml:"description"`
+	Inputs      map[string]*ActionYAMLInput `yaml:"inputs"`
+}
+
+// actionRef holds the parsed components of a GitHub Action `uses` field.
+type actionRef struct {
+	// Repo is the GitHub repository slug (e.g., "owner/repo").
+	Repo string
+	// Subdir is the sub-directory within the repository (e.g., "path/to/action").
+	// Empty string means the action.yml is at the repository root.
+	Subdir string
+	// Ref is the git ref (tag, SHA, or branch) to checkout (e.g., "v1", "main").
+	Ref string
+	// IsLocal is true when the `uses` value is a local path (e.g., "./path/to/action").
+	IsLocal bool
+	// LocalPath is the filesystem path (only set when IsLocal is true).
+	LocalPath string
+}
+
+// parseActionsConfig parses the safe-outputs.actions section from a raw frontmatter map.
+// It returns a map of action names to their configurations.
+func parseActionsConfig(actionsMap map[string]any) map[string]*SafeOutputActionConfig {
+	if actionsMap == nil {
+		return nil
+	}
+
+	result := make(map[string]*SafeOutputActionConfig)
+	for actionName, actionValue := range actionsMap {
+		actionConfigMap, ok := actionValue.(map[string]any)
+		if !ok {
+			safeOutputActionsLog.Printf("Warning: action %q config is not a map, skipping", actionName)
+			continue
+		}
+
+		actionConfig := &SafeOutputActionConfig{}
+
+		if uses, ok := actionConfigMap["uses"].(string); ok {
+			actionConfig.Uses = uses
+		}
+		if description, ok := actionConfigMap["description"].(string); ok {
+			actionConfig.Description = description
+		}
+
+		if actionConfig.Uses == "" {
+			safeOutputActionsLog.Printf("Warning: action %q is missing required 'uses' field, skipping", actionName)
+			continue
+		}
+
+		result[actionName] = actionConfig
+	}
+
+	return result
+}
+
+// parseActionUsesField parses a GitHub Action `uses` field into its components.
+// Supported formats:
+//   - "owner/repo@ref"              -> repo root action
+//   - "owner/repo/subdir@ref"       -> sub-directory action
+//   - "./local/path"                -> local filesystem action
+func parseActionUsesField(uses string) (*actionRef, error) {
+	if strings.HasPrefix(uses, "./") || strings.HasPrefix(uses, "../") {
+		return &actionRef{IsLocal: true, LocalPath: uses}, nil
+	}
+
+	// External action: split on "@" to get ref
+	atIdx := strings.LastIndex(uses, "@")
+	if atIdx < 0 {
+		return nil, fmt.Errorf("invalid action ref %q: missing @ref suffix", uses)
+	}
+
+	refStr := uses[atIdx+1:]
+	repoAndPath := uses[:atIdx]
+
+	// Split repo from subdir: first two path segments are owner/repo
+	parts := strings.SplitN(repoAndPath, "/", 3)
+	if len(parts) < 2 {
+		return nil, fmt.Errorf("invalid action ref %q: expected owner/repo format", uses)
+	}
+
+	repo := parts[0] + "/" + parts[1]
+	var subdir string
+	if len(parts) == 3 {
+		subdir = parts[2]
+	}
+
+	return &actionRef{
+		Repo:   repo,
+		Subdir: subdir,
+		Ref:    refStr,
+	}, nil
+}
+
+// fetchAndParseActionYAML resolves the inputs and description from the action.yml
+// for each configured action. Results are stored in the action config's computed fields.
+// This function should be called before tool generation and step generation.
+func (c *Compiler) fetchAndParseActionYAML(actionName string, config *SafeOutputActionConfig, markdownPath string, data *WorkflowData) {
+	if config.Uses == "" {
+		return
+	}
+
+	ref, err := parseActionUsesField(config.Uses)
+	if err != nil {
+		safeOutputActionsLog.Printf("Warning: failed to parse uses field %q for action %q: %v", config.Uses, actionName, err)
+		return
+	}
+
+	var actionYAML *actionYAMLFile
+	var resolvedRef string
+
+	if ref.IsLocal {
+		actionYAML, err = readLocalActionYAML(ref.LocalPath, markdownPath)
+		if err != nil {
+			safeOutputActionsLog.Printf("Warning: failed to read local action.yml for %q at %s: %v", actionName, ref.LocalPath, err)
+		}
+		resolvedRef = config.Uses // local paths stay as-is
+	} else {
+		// Pin the action ref and fetch the action.yml
+		pinned, pinErr := GetActionPinWithData(ref.Repo, ref.Ref, data)
+		if pinErr != nil {
+			safeOutputActionsLog.Printf("Warning: failed to pin action %q (%s@%s): %v", actionName, ref.Repo, ref.Ref, pinErr)
+			// Fall back to using the original ref
+			resolvedRef = config.Uses
+		} else {
+			resolvedRef = pinned
+		}
+
+		actionYAML, err = fetchRemoteActionYAML(ref.Repo, ref.Subdir, ref.Ref)
+		if err != nil {
+			safeOutputActionsLog.Printf("Warning: failed to fetch action.yml for %q (%s): %v", actionName, config.Uses, err)
+		}
+	}
+
+	config.ResolvedRef = resolvedRef
+
+	if actionYAML != nil {
+		config.Inputs = actionYAML.Inputs
+		config.ActionDescription = actionYAML.Description
+	}
+}
+
+// fetchRemoteActionYAML fetches and parses action.yml from a GitHub repository.
+// It tries both action.yml and action.yaml filenames.
+func fetchRemoteActionYAML(repo, subdir, ref string) (*actionYAMLFile, error) {
+	for _, filename := range []string{"action.yml", "action.yaml"} {
+		var contentPath string
+		if subdir != "" {
+			contentPath = subdir + "/" + filename
+		} else {
+			contentPath = filename
+		}
+
+		apiPath := fmt.Sprintf("/repos/%s/contents/%s?ref=%s", repo, contentPath, ref)
+		safeOutputActionsLog.Printf("Fetching action YAML from: %s", apiPath)
+
+		ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
+		cmd := ExecGHContext(ctx, "api", apiPath, "--jq", ".content")
+		output, err := cmd.Output()
+		cancel()
+		if err != nil {
+			safeOutputActionsLog.Printf("Failed to fetch %s from %s@%s: %v", filename, repo, ref, err)
+			continue
+		}
+
+		// GitHub API returns base64-encoded content with newlines
+		b64Content := strings.ReplaceAll(strings.TrimSpace(string(output)), "\\n", "\n")
+		// Remove JSON string quotes if present
+		b64Content = strings.Trim(b64Content, "\"")
+		decoded, decErr := base64.StdEncoding.DecodeString(b64Content)
+		if decErr != nil {
+			safeOutputActionsLog.Printf("Failed to decode content for %s: %v", contentPath, decErr)
+			continue
+		}
+
+		actionYAML, parseErr := parseActionYAMLContent(decoded)
+		if parseErr != nil {
+			safeOutputActionsLog.Printf("Failed to parse %s: %v", contentPath, parseErr)
+			continue
+		}
+
+		return actionYAML, nil
+	}
+
+	return nil, fmt.Errorf("could not find action.yml or action.yaml in %s@%s (subdir=%q)", repo, ref, subdir)
+}
+
+// readLocalActionYAML reads and parses a local action.yml file.
+func readLocalActionYAML(localPath, markdownPath string) (*actionYAMLFile, error) {
+	baseDir := filepath.Dir(markdownPath)
+
+	// Strip leading "./" from the local path
+	cleanPath := strings.TrimPrefix(localPath, "./")
+	actionDir := filepath.Join(baseDir, cleanPath)
+
+	for _, filename := range []string{"action.yml", "action.yaml"} {
+		fullPath := filepath.Join(actionDir, filename)
+		content, err := os.ReadFile(fullPath)
+		if err != nil {
+			continue
+		}
+		return parseActionYAMLContent(content)
+	}
+
+	return nil, fmt.Errorf("could not find action.yml or action.yaml at %s", actionDir)
+}
+
+// parseActionYAMLContent parses raw action.yml YAML content.
+func parseActionYAMLContent(content []byte) (*actionYAMLFile, error) {
+	var parsed actionYAMLFile
+	if err := yaml.Unmarshal(content, &parsed); err != nil {
+		return nil, fmt.Errorf("failed to parse action YAML: %w", err)
+	}
+	return &parsed, nil
+}
+
+// generateActionToolDefinition creates an MCP tool definition for a custom safe output action.
+// The tool name is the normalized action name. Inputs are derived from the action.yml.
+func generateActionToolDefinition(actionName string, config *SafeOutputActionConfig) map[string]any {
+	normalizedName := stringutil.NormalizeSafeOutputIdentifier(actionName)
+
+	description := config.Description
+	if description == "" {
+		description = config.ActionDescription
+	}
+	if description == "" {
+		description = fmt.Sprintf("Run the %s action", actionName)
+	}
+	// Append once-only constraint to description
+	description += " (can only be called once)"
+
+	inputSchema := map[string]any{
+		"type":                 "object",
+		"properties":           make(map[string]any),
+		"additionalProperties": false,
+	}
+
+	var requiredFields []string
+	properties := inputSchema["properties"].(map[string]any)
+
+	if config.Inputs != nil {
+		// Sort for deterministic output
+		inputNames := make([]string, 0, len(config.Inputs))
+		for k := range config.Inputs {
+			inputNames = append(inputNames, k)
+		}
+		sort.Strings(inputNames)
+
+		for _, inputName := range inputNames {
+			inputDef := config.Inputs[inputName]
+			property := map[string]any{
+				"type": "string",
+			}
+			if inputDef.Description != "" {
+				property["description"] = inputDef.Description
+			}
+			if inputDef.Default != "" {
+				property["default"] = inputDef.Default
+			}
+			if inputDef.Required {
+				requiredFields = append(requiredFields, inputName)
+			}
+			properties[inputName] = property
+		}
+	}
+
+	if len(requiredFields) > 0 {
+		sort.Strings(requiredFields)
+		inputSchema["required"] = requiredFields
+	}
+
+	return map[string]any{
+		"name":        normalizedName,
+		"description": description,
+		"inputSchema": inputSchema,
+	}
+}
+
+// buildCustomSafeOutputActionsJSON builds a JSON mapping of normalized action names
+// used for the GH_AW_SAFE_OUTPUT_ACTIONS env var of the handler manager step.
+// This allows the handler manager to load and dispatch messages to action handlers.
+// The map value is the normalized action name (same as key) for future extensibility.
+func buildCustomSafeOutputActionsJSON(data *WorkflowData) string {
+	if data.SafeOutputs == nil || len(data.SafeOutputs.Actions) == 0 {
+		return ""
+	}
+
+	actionMapping := make(map[string]string, len(data.SafeOutputs.Actions))
+	for actionName := range data.SafeOutputs.Actions {
+		normalizedName := stringutil.NormalizeSafeOutputIdentifier(actionName)
+		actionMapping[normalizedName] = normalizedName
+	}
+
+	// Sort keys for deterministic output
+	keys := make([]string, 0, len(actionMapping))
+	for k := range actionMapping {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+
+	ordered := make(map[string]string, len(keys))
+	for _, k := range keys {
+		ordered[k] = actionMapping[k]
+	}
+
+	jsonBytes, err := json.Marshal(ordered)
+	if err != nil {
+		safeOutputActionsLog.Printf("Warning: failed to marshal custom safe output actions: %v", err)
+		return ""
+	}
+	return string(jsonBytes)
+}
+
+// actionOutputKey returns the step output key for a given normalized action name.
+// The handler exports this key and the compiler uses it in step conditions and with: blocks.
+func actionOutputKey(normalizedName string) string {
+	return "action_" + normalizedName + "_payload"
+}
+
+// buildActionSteps generates the YAML steps for all configured safe output actions.
+// Each step:
+//   - Is guarded by an `if:` condition checking the payload output from process_safe_outputs
+//   - Uses the resolved action reference
+//   - Has a `with:` block populated from parsed payload output via fromJSON
+func (c *Compiler) buildActionSteps(data *WorkflowData) []string {
+	if data.SafeOutputs == nil || len(data.SafeOutputs.Actions) == 0 {
+		return nil
+	}
+
+	// Sort action names for deterministic output
+	actionNames := make([]string, 0, len(data.SafeOutputs.Actions))
+	for name := range data.SafeOutputs.Actions {
+		actionNames = append(actionNames, name)
+	}
+	sort.Strings(actionNames)
+
+	var steps []string
+
+	for _, actionName := range actionNames {
+		config := data.SafeOutputs.Actions[actionName]
+		normalizedName := stringutil.NormalizeSafeOutputIdentifier(actionName)
+		outputKey := actionOutputKey(normalizedName)
+
+		// Determine the action reference to use in the step
+		actionRef := config.ResolvedRef
+		if actionRef == "" {
+			// Fall back to original uses value if resolution failed
+			actionRef = config.Uses
+		}
+
+		// Display name: prefer the user description, then action description, then action name
+		displayName := config.Description
+		if displayName == "" {
+			displayName = config.ActionDescription
+		}
+		if displayName == "" {
+			displayName = actionName
+		}
+
+		steps = append(steps, fmt.Sprintf("      - name: %s\n", displayName))
+		steps = append(steps, fmt.Sprintf("        id: action_%s\n", normalizedName))
+		steps = append(steps, fmt.Sprintf("        if: steps.process_safe_outputs.outputs.%s != ''\n", outputKey))
+		steps = append(steps, fmt.Sprintf("        uses: %s\n", actionRef))
+
+		// Build the with: block
+		if len(config.Inputs) > 0 {
+			steps = append(steps, "        with:\n")
+
+			inputNames := make([]string, 0, len(config.Inputs))
+			for k := range config.Inputs {
+				inputNames = append(inputNames, k)
+			}
+			sort.Strings(inputNames)
+
+			for _, inputName := range inputNames {
+				steps = append(steps, fmt.Sprintf("          %s: ${{ fromJSON(steps.process_safe_outputs.outputs.%s).%s }}\n",
+					inputName, outputKey, inputName))
+			}
+		} else {
+			// When inputs couldn't be resolved, pass the raw payload as a single input
+			steps = append(steps, "        with:\n")
+			steps = append(steps, fmt.Sprintf("          payload: ${{ steps.process_safe_outputs.outputs.%s }}\n", outputKey))
+		}
+	}
+
+	return steps
+}
+
+// resolveAllActions fetches action.yml for all configured actions and populates
+// the computed fields (ResolvedRef, Inputs, ActionDescription) in each config.
+// This should be called once during compilation before tool generation and step generation.
+func (c *Compiler) resolveAllActions(data *WorkflowData, markdownPath string) {
+	if data.SafeOutputs == nil || len(data.SafeOutputs.Actions) == 0 {
+		return
+	}
+
+	safeOutputActionsLog.Printf("Resolving %d custom safe output action(s)", len(data.SafeOutputs.Actions))
+	for actionName, config := range data.SafeOutputs.Actions {
+		if config.ResolvedRef != "" {
+			// Already resolved (e.g., called multiple times)
+			continue
+		}
+		c.fetchAndParseActionYAML(actionName, config, markdownPath, data)
+		safeOutputActionsLog.Printf("Resolved action %q: ref=%q, inputs=%d", actionName, config.ResolvedRef, len(config.Inputs))
+	}
+}
diff --git a/pkg/workflow/safe_outputs_actions_test.go b/pkg/workflow/safe_outputs_actions_test.go
new file mode 100644
index 00000000000..5b09ff75231
--- /dev/null
+++ b/pkg/workflow/safe_outputs_actions_test.go
@@ -0,0 +1,417 @@
+//go:build !integration
+
+package workflow
+
+import (
+	"encoding/json"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+// TestParseActionsConfig verifies parsing of safe-outputs.actions configuration
+func TestParseActionsConfig(t *testing.T) {
+	actionsMap := map[string]any{
+		"my-tool": map[string]any{
+			"uses":        "owner/repo@v1",
+			"description": "My custom tool",
+		},
+		"another-tool": map[string]any{
+			"uses": "owner/other-repo/subdir@v2",
+		},
+	}
+
+	result := parseActionsConfig(actionsMap)
+
+	require.NotNil(t, result, "Should return non-nil result")
+	require.Len(t, result, 2, "Should have two actions")
+
+	myTool, exists := result["my-tool"]
+	require.True(t, exists, "Should have my-tool action")
+	assert.Equal(t, "owner/repo@v1", myTool.Uses, "Uses should match")
+	assert.Equal(t, "My custom tool", myTool.Description, "Description should match")
+
+	anotherTool, exists := result["another-tool"]
+	require.True(t, exists, "Should have another-tool action")
+	assert.Equal(t, "owner/other-repo/subdir@v2", anotherTool.Uses, "Uses should match")
+	assert.Empty(t, anotherTool.Description, "Description should be empty")
+}
+
+// TestParseActionsConfigMissingUses verifies actions without uses are skipped
+func TestParseActionsConfigMissingUses(t *testing.T) {
+	actionsMap := map[string]any{
+		"invalid-tool": map[string]any{
+			"description": "No uses field",
+		},
+		"valid-tool": map[string]any{
+			"uses": "owner/repo@v1",
+		},
+	}
+
+	result := parseActionsConfig(actionsMap)
+
+	require.Len(t, result, 1, "Should only have the valid action")
+	_, exists := result["invalid-tool"]
+	assert.False(t, exists, "Should not have invalid-tool (missing uses)")
+	_, exists = result["valid-tool"]
+	assert.True(t, exists, "Should have valid-tool")
+}
+
+// TestParseActionsConfigEmpty verifies empty map returns nil or empty
+func TestParseActionsConfigEmpty(t *testing.T) {
+	result := parseActionsConfig(map[string]any{})
+	assert.Empty(t, result, "Should return empty result for empty map")
+
+	result = parseActionsConfig(nil)
+	assert.Nil(t, result, "Should return nil for nil input")
+}
+
+// TestParseActionUsesField verifies parsing of different uses field formats
+func TestParseActionUsesField(t *testing.T) {
+	tests := []struct {
+		name       string
+		uses       string
+		wantRepo   string
+		wantSubdir string
+		wantRef    string
+		wantLocal  bool
+		wantErr    bool
+	}{
+		{
+			name:     "root action",
+			uses:     "owner/repo@v1",
+			wantRepo: "owner/repo",
+			wantRef:  "v1",
+		},
+		{
+			name:       "sub-directory action",
+			uses:       "owner/repo/subpath@v2",
+			wantRepo:   "owner/repo",
+			wantSubdir: "subpath",
+			wantRef:    "v2",
+		},
+		{
+			name:       "deep sub-directory action",
+			uses:       "owner/repo/path/to/action@v3",
+			wantRepo:   "owner/repo",
+			wantSubdir: "path/to/action",
+			wantRef:    "v3",
+		},
+		{
+			name:      "local path",
+			uses:      "./local/path",
+			wantLocal: true,
+		},
+		{
+			name:      "local path with parent",
+			uses:      "../parent/path",
+			wantLocal: true,
+		},
+		{
+			name:    "missing ref",
+			uses:    "owner/repo",
+			wantErr: true,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ref, err := parseActionUsesField(tt.uses)
+			if tt.wantErr {
+				assert.Error(t, err, "Should return error")
+				return
+			}
+			require.NoError(t, err, "Should not return error")
+			assert.Equal(t, tt.wantLocal, ref.IsLocal, "IsLocal mismatch")
+			if tt.wantLocal {
+				return
+			}
+			assert.Equal(t, tt.wantRepo, ref.Repo, "Repo mismatch")
+			assert.Equal(t, tt.wantSubdir, ref.Subdir, "Subdir mismatch")
+			assert.Equal(t, tt.wantRef, ref.Ref, "Ref mismatch")
+		})
+	}
+}
+
+// TestGenerateActionToolDefinition verifies tool definition generation
+func TestGenerateActionToolDefinition(t *testing.T) {
+	config := &SafeOutputActionConfig{
+		Uses:              "owner/repo@v1",
+		Description:       "My custom action tool",
+		ResolvedRef:       "owner/repo@abc123 # v1",
+		ActionDescription: "Default description from action.yml",
+		Inputs: map[string]*ActionYAMLInput{
+			"title": {
+				Description: "The title input",
+				Required:    true,
+			},
+			"body": {
+				Description: "The body input",
+				Required:    false,
+				Default:     "default body",
+			},
+		},
+	}
+
+	tool := generateActionToolDefinition("my-tool", config)
+
+	require.NotNil(t, tool, "Tool definition should not be nil")
+	assert.Equal(t, "my_tool", tool["name"], "Tool name should be normalized")
+	assert.Equal(t, "My custom action tool (can only be called once)", tool["description"], "Description should use user override + constraint")
+
+	schema, ok := tool["inputSchema"].(map[string]any)
+	require.True(t, ok, "inputSchema should be a map")
+	assert.Equal(t, "object", schema["type"])
+	assert.Equal(t, false, schema["additionalProperties"])
+
+	properties, ok := schema["properties"].(map[string]any)
+	require.True(t, ok, "properties should be a map")
+	assert.Len(t, properties, 2, "Should have 2 properties")
+
+	titleProp, ok := properties["title"].(map[string]any)
+	require.True(t, ok, "title property should exist")
+	assert.Equal(t, "string", titleProp["type"])
+	assert.Equal(t, "The title input", titleProp["description"])
+
+	bodyProp, ok := properties["body"].(map[string]any)
+	require.True(t, ok, "body property should exist")
+	assert.Equal(t, "string", bodyProp["type"])
+	assert.Equal(t, "default body", bodyProp["default"])
+
+	required, ok := schema["required"].([]string)
+	require.True(t, ok, "required should be a []string")
+	assert.Equal(t, []string{"title"}, required, "Only title should be required")
+}
+
+// TestGenerateActionToolDefinitionFallbackDescription verifies description fallback order
+func TestGenerateActionToolDefinitionFallbackDescription(t *testing.T) {
+	tests := []struct {
+		name            string
+		config          *SafeOutputActionConfig
+		wantDescription string
+	}{
+		{
+			name: "user description takes precedence",
+			config: &SafeOutputActionConfig{
+				Description:       "User description",
+				ActionDescription: "Action description",
+			},
+			wantDescription: "User description (can only be called once)",
+		},
+		{
+			name: "action description used when no user description",
+			config: &SafeOutputActionConfig{
+				ActionDescription: "Action description",
+			},
+			wantDescription: "Action description (can only be called once)",
+		},
+		{
+			name:            "fallback to action name",
+			config:          &SafeOutputActionConfig{},
+			wantDescription: "Run the my-action action (can only be called once)",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tool := generateActionToolDefinition("my-action", tt.config)
+			assert.Equal(t, tt.wantDescription, tool["description"], "Description should match expected")
+		})
+	}
+}
+
+// TestGenerateActionToolDefinitionNoInputs verifies tool with no inputs (resolution failed)
+func TestGenerateActionToolDefinitionNoInputs(t *testing.T) {
+	config := &SafeOutputActionConfig{
+		Uses: "owner/repo@v1",
+	}
+
+	tool := generateActionToolDefinition("my-tool", config)
+
+	schema, ok := tool["inputSchema"].(map[string]any)
+	require.True(t, ok, "inputSchema should be a map")
+	properties, ok := schema["properties"].(map[string]any)
+	require.True(t, ok, "properties should be a map")
+	assert.Empty(t, properties, "No properties when inputs unknown")
+	assert.Nil(t, schema["required"], "No required fields when inputs unknown")
+}
+
+// TestBuildCustomSafeOutputActionsJSON verifies JSON generation for GH_AW_SAFE_OUTPUT_ACTIONS
+func TestBuildCustomSafeOutputActionsJSON(t *testing.T) {
+	data := &WorkflowData{
+		SafeOutputs: &SafeOutputsConfig{
+			Actions: map[string]*SafeOutputActionConfig{
+				"my-tool":      {Uses: "owner/repo@v1"},
+				"another-tool": {Uses: "owner/repo2@v2"},
+			},
+		},
+	}
+
+	jsonStr := buildCustomSafeOutputActionsJSON(data)
+	require.NotEmpty(t, jsonStr, "Should return non-empty JSON")
+
+	var result map[string]string
+	err := json.Unmarshal([]byte(jsonStr), &result)
+	require.NoError(t, err, "Should be valid JSON")
+
+	assert.Equal(t, "my_tool", result["my_tool"], "Normalized name should be in map")
+	assert.Equal(t, "another_tool", result["another_tool"], "Normalized name should be in map")
+}
+
+// TestBuildCustomSafeOutputActionsJSONEmpty verifies empty result when no actions
+func TestBuildCustomSafeOutputActionsJSONEmpty(t *testing.T) {
+	assert.Empty(t, buildCustomSafeOutputActionsJSON(&WorkflowData{SafeOutputs: &SafeOutputsConfig{}}), "Should return empty for no actions")
+	assert.Empty(t, buildCustomSafeOutputActionsJSON(&WorkflowData{SafeOutputs: nil}), "Should return empty for nil SafeOutputs")
+}
+
+// TestBuildActionSteps verifies step generation for configured actions
+func TestBuildActionSteps(t *testing.T) {
+	compiler := NewCompiler()
+	data := &WorkflowData{
+		SafeOutputs: &SafeOutputsConfig{
+			Actions: map[string]*SafeOutputActionConfig{
+				"my-tool": {
+					Uses:        "owner/repo@v1",
+					Description: "My tool description",
+					ResolvedRef: "owner/repo@abc123 # v1",
+					Inputs: map[string]*ActionYAMLInput{
+						"title": {Required: true},
+						"body":  {Required: false},
+					},
+				},
+			},
+		},
+	}
+
+	steps := compiler.buildActionSteps(data)
+	require.NotEmpty(t, steps, "Should generate steps")
+
+	fullYAML := strings.Join(steps, "")
+	assert.Contains(t, fullYAML, "My tool description", "Should use description as step name")
+	assert.Contains(t, fullYAML, "id: action_my_tool", "Should have step ID")
+	assert.Contains(t, fullYAML, "steps.process_safe_outputs.outputs.action_my_tool_payload != ''", "Should have if condition")
+	assert.Contains(t, fullYAML, "uses: owner/repo@abc123 # v1", "Should use resolved ref")
+	assert.Contains(t, fullYAML, "title:", "Should have title input")
+	assert.Contains(t, fullYAML, "body:", "Should have body input")
+	assert.Contains(t, fullYAML, "fromJSON(steps.process_safe_outputs.outputs.action_my_tool_payload).title", "Should use fromJSON for title")
+	assert.Contains(t, fullYAML, "fromJSON(steps.process_safe_outputs.outputs.action_my_tool_payload).body", "Should use fromJSON for body")
+}
+
+// TestBuildActionStepsFallbackPayload verifies step uses payload when inputs unknown
+func TestBuildActionStepsFallbackPayload(t *testing.T) {
+	compiler := NewCompiler()
+	data := &WorkflowData{
+		SafeOutputs: &SafeOutputsConfig{
+			Actions: map[string]*SafeOutputActionConfig{
+				"unknown-inputs-tool": {
+					Uses:        "owner/repo@v1",
+					ResolvedRef: "owner/repo@abc123 # v1",
+					// No Inputs: action.yml couldn't be fetched
+				},
+			},
+		},
+	}
+
+	steps := compiler.buildActionSteps(data)
+	require.NotEmpty(t, steps, "Should generate steps even without inputs")
+
+	fullYAML := strings.Join(steps, "")
+	assert.Contains(t, fullYAML, "payload:", "Should use payload as single with: key when inputs unknown")
+	assert.Contains(t, fullYAML, "steps.process_safe_outputs.outputs.action_unknown_inputs_tool_payload", "Should reference payload output")
+}
+
+// TestBuildActionStepsEmpty verifies no steps when no actions
+func TestBuildActionStepsEmpty(t *testing.T) {
+	compiler := NewCompiler()
+	data := &WorkflowData{
+		SafeOutputs: &SafeOutputsConfig{},
+	}
+	assert.Nil(t, compiler.buildActionSteps(data), "Should return nil for empty actions")
+}
+
+// TestExtractSafeOutputsConfigIncludesActions verifies extractSafeOutputsConfig handles actions
+func TestExtractSafeOutputsConfigIncludesActions(t *testing.T) {
+	compiler := NewCompiler()
+	frontmatter := map[string]any{
+		"safe-outputs": map[string]any{
+			"actions": map[string]any{
+				"my-action": map[string]any{
+					"uses":        "owner/repo@v1",
+					"description": "My action",
+				},
+			},
+		},
+	}
+
+	config := compiler.extractSafeOutputsConfig(frontmatter)
+
+	require.NotNil(t, config, "Should extract config")
+	require.Len(t, config.Actions, 1, "Should have 1 action")
+
+	action, exists := config.Actions["my-action"]
+	require.True(t, exists, "Should have my-action")
+	assert.Equal(t, "owner/repo@v1", action.Uses, "Uses should match")
+	assert.Equal(t, "My action", action.Description, "Description should match")
+}
+
+// TestHandlerManagerStepIncludesActionsEnvVar verifies GH_AW_SAFE_OUTPUT_ACTIONS env var
+func TestHandlerManagerStepIncludesActionsEnvVar(t *testing.T) {
+	compiler := NewCompiler()
+	workflowData := &WorkflowData{
+		SafeOutputs: &SafeOutputsConfig{
+			CreateIssues: &CreateIssuesConfig{},
+			Actions: map[string]*SafeOutputActionConfig{
+				"my-action": {Uses: "owner/repo@v1"},
+			},
+		},
+	}
+
+	steps := compiler.buildHandlerManagerStep(workflowData)
+	fullYAML := strings.Join(steps, "")
+
+	assert.Contains(t, fullYAML, "GH_AW_SAFE_OUTPUT_ACTIONS", "Should include GH_AW_SAFE_OUTPUT_ACTIONS env var")
+	assert.Contains(t, fullYAML, "my_action", "Should include normalized action name")
+}
+
+// TestHandlerManagerStepNoActionsEnvVar verifies GH_AW_SAFE_OUTPUT_ACTIONS absent when no actions
+func TestHandlerManagerStepNoActionsEnvVar(t *testing.T) {
+	compiler := NewCompiler()
+	workflowData := &WorkflowData{
+		SafeOutputs: &SafeOutputsConfig{
+			CreateIssues: &CreateIssuesConfig{},
+		},
+	}
+
+	steps := compiler.buildHandlerManagerStep(workflowData)
+	fullYAML := strings.Join(steps, "")
+
+	assert.NotContains(t, fullYAML, "GH_AW_SAFE_OUTPUT_ACTIONS", "Should not include GH_AW_SAFE_OUTPUT_ACTIONS when no actions")
+}
+
+// TestHasAnySafeOutputEnabledWithActions verifies Actions are detected as enabled
+func TestHasAnySafeOutputEnabledWithActions(t *testing.T) {
+	config := &SafeOutputsConfig{
+		Actions: map[string]*SafeOutputActionConfig{
+			"my-action": {Uses: "owner/repo@v1"},
+		},
+	}
+	assert.True(t, hasAnySafeOutputEnabled(config), "Should detect actions as enabled safe outputs")
+}
+
+// TestHasNonBuiltinSafeOutputsEnabledWithActions verifies Actions count as non-builtin
+func TestHasNonBuiltinSafeOutputsEnabledWithActions(t *testing.T) {
+	config := &SafeOutputsConfig{
+		Actions: map[string]*SafeOutputActionConfig{
+			"my-action": {Uses: "owner/repo@v1"},
+		},
+	}
+	assert.True(t, hasNonBuiltinSafeOutputsEnabled(config), "Actions should count as non-builtin safe outputs")
+}
+
+// TestActionOutputKey verifies the output key naming convention
+func TestActionOutputKey(t *testing.T) {
+	assert.Equal(t, "action_my_tool_payload", actionOutputKey("my_tool"))
+	assert.Equal(t, "action_another_action_payload", actionOutputKey("another_action"))
+}
diff --git a/pkg/workflow/safe_outputs_config.go b/pkg/workflow/safe_outputs_config.go
index d0c124a7ff5..70146ca9fc3 100644
--- a/pkg/workflow/safe_outputs_config.go
+++ b/pkg/workflow/safe_outputs_config.go
@@ -539,6 +539,14 @@ func (c *Compiler) extractSafeOutputsConfig(frontmatter map[string]any) *SafeOut
 				}
 			}
 
+			// Handle actions (custom GitHub Actions mounted as safe output tools)
+			if actions, exists := outputMap["actions"]; exists {
+				if actionsMap, ok := actions.(map[string]any); ok {
+					config.Actions = parseActionsConfig(actionsMap)
+					safeOutputsConfigLog.Printf("Configured %d custom safe-output action(s)", len(config.Actions))
+				}
+			}
+
 			// Handle app configuration for GitHub App token minting
 			if app, exists := outputMap["github-app"]; exists {
 				if appMap, ok := app.(map[string]any); ok {
diff --git a/pkg/workflow/safe_outputs_state.go b/pkg/workflow/safe_outputs_state.go
index 267c1632512..a8efdd51e9d 100644
--- a/pkg/workflow/safe_outputs_state.go
+++ b/pkg/workflow/safe_outputs_state.go
@@ -84,6 +84,12 @@ func hasAnySafeOutputEnabled(safeOutputs *SafeOutputsConfig) bool {
 		return true
 	}
 
+	// Check Actions separately as it's a map
+	if len(safeOutputs.Actions) > 0 {
+		safeOutputReflectionLog.Printf("Found %d custom actions enabled", len(safeOutputs.Actions))
+		return true
+	}
+
 	// Use reflection to check all pointer fields
 	val := reflect.ValueOf(safeOutputs).Elem()
 	for fieldName := range safeOutputFieldMapping {
@@ -136,6 +142,11 @@ func hasNonBuiltinSafeOutputsEnabled(safeOutputs *SafeOutputsConfig) bool {
 		return true
 	}
 
+	// Custom actions are always non-builtin
+	if len(safeOutputs.Actions) > 0 {
+		return true
+	}
+
 	// Check non-builtin pointer fields using the pre-computed list
 	val := reflect.ValueOf(safeOutputs).Elem()
 	for _, fieldName := range nonBuiltinSafeOutputFieldNames {
diff --git a/pkg/workflow/safe_outputs_tools_filtering.go b/pkg/workflow/safe_outputs_tools_filtering.go
index 08f83190dae..6533dfea767 100644
--- a/pkg/workflow/safe_outputs_tools_filtering.go
+++ b/pkg/workflow/safe_outputs_tools_filtering.go
@@ -249,8 +249,25 @@ func generateFilteredToolsJSON(data *WorkflowData, markdownPath string) (string,
 		}
 	}
 
+	// Add custom action tools from SafeOutputs.Actions
+	if len(data.SafeOutputs.Actions) > 0 {
+		safeOutputsConfigLog.Printf("Adding %d custom action tools", len(data.SafeOutputs.Actions))
+
+		actionNames := make([]string, 0, len(data.SafeOutputs.Actions))
+		for actionName := range data.SafeOutputs.Actions {
+			actionNames = append(actionNames, actionName)
+		}
+		sort.Strings(actionNames)
+
+		for _, actionName := range actionNames {
+			actionConfig := data.SafeOutputs.Actions[actionName]
+			customTool := generateActionToolDefinition(actionName, actionConfig)
+			filteredTools = append(filteredTools, customTool)
+		}
+	}
+
 	if safeOutputsConfigLog.Enabled() {
-		safeOutputsConfigLog.Printf("Filtered %d tools from %d total tools (including %d custom jobs, %d custom scripts)", len(filteredTools), len(allTools), len(data.SafeOutputs.Jobs), len(data.SafeOutputs.Scripts))
+		safeOutputsConfigLog.Printf("Filtered %d tools from %d total tools (including %d custom jobs, %d custom scripts, %d custom actions)", len(filteredTools), len(allTools), len(data.SafeOutputs.Jobs), len(data.SafeOutputs.Scripts), len(data.SafeOutputs.Actions))
 	}
 
 	// Add dynamic dispatch_workflow tools
@@ -763,6 +780,26 @@ func generateDynamicTools(data *WorkflowData, markdownPath string) ([]map[string
 		}
 	}
 
+	// Add custom action tools from SafeOutputs.Actions
+	// Each configured action is exposed as an MCP tool with schema derived from action.yml.
+	// The compiler resolves action.yml at compile time; if resolution fails the tool is still
+	// added with an empty inputSchema so the agent can still attempt to call it.
+	if len(data.SafeOutputs.Actions) > 0 {
+		safeOutputsConfigLog.Printf("Adding %d custom action tools to dynamic tools", len(data.SafeOutputs.Actions))
+
+		actionNames := make([]string, 0, len(data.SafeOutputs.Actions))
+		for actionName := range data.SafeOutputs.Actions {
+			actionNames = append(actionNames, actionName)
+		}
+		sort.Strings(actionNames)
+
+		for _, actionName := range actionNames {
+			actionConfig := data.SafeOutputs.Actions[actionName]
+			customTool := generateActionToolDefinition(actionName, actionConfig)
+			dynamicTools = append(dynamicTools, customTool)
+		}
+	}
+
 	// Add dynamic dispatch_workflow tools
 	if data.SafeOutputs.DispatchWorkflow != nil && len(data.SafeOutputs.DispatchWorkflow.Workflows) > 0 {
 		safeOutputsConfigLog.Printf("Adding %d dispatch_workflow tools", len(data.SafeOutputs.DispatchWorkflow.Workflows))

From b55023829bf24b99540ec4414daa6d5c15280b34 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 07:12:41 +0000
Subject: [PATCH 03/17] fix: address code review comments - fix base64 decoding
 and use INTERNAL_MESSAGE_FIELDS set

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/safe_output_action_handler.cjs | 15 +++++++++++++--
 pkg/workflow/safe_outputs_actions.go            | 13 +++++++++----
 2 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/actions/setup/js/safe_output_action_handler.cjs b/actions/setup/js/safe_output_action_handler.cjs
index 836d04c0b2c..da002f76fed 100644
--- a/actions/setup/js/safe_output_action_handler.cjs
+++ b/actions/setup/js/safe_output_action_handler.cjs
@@ -8,6 +8,15 @@
 const { replaceTemporaryIdReferences } = require("./temporary_id.cjs");
 const { getErrorMessage } = require("./error_helpers.cjs");
 
+/**
+ * Internal safe-output message fields that should not be forwarded as action inputs.
+ * These fields are part of the safe-output messaging protocol and are not user-defined.
+ * Maintained as an explicit set so future protocol fields can be added without silently
+ * forwarding them to external action `with:` inputs.
+ * @type {Set<string>}
+ */
+const INTERNAL_MESSAGE_FIELDS = new Set(["type"]);
+
 /**
  * Main handler factory for a custom safe output action.
  *
@@ -65,8 +74,10 @@ async function main(config = {}) {
       // are passed through as-is.
       const processedInputs = {};
       for (const [key, value] of Object.entries(message)) {
-        // Skip internal fields that are not action inputs
-        if (key === "type") {
+        // Skip internal safe-output messaging fields that are not action inputs.
+        // Maintained as an explicit set to allow future additions without silently
+        // forwarding new internal fields to external action steps.
+        if (INTERNAL_MESSAGE_FIELDS.has(key)) {
           continue;
         }
 
diff --git a/pkg/workflow/safe_outputs_actions.go b/pkg/workflow/safe_outputs_actions.go
index ff4d8364251..f5e245e1d97 100644
--- a/pkg/workflow/safe_outputs_actions.go
+++ b/pkg/workflow/safe_outputs_actions.go
@@ -204,10 +204,15 @@ func fetchRemoteActionYAML(repo, subdir, ref string) (*actionYAMLFile, error) {
 			continue
 		}
 
-		// GitHub API returns base64-encoded content with newlines
-		b64Content := strings.ReplaceAll(strings.TrimSpace(string(output)), "\\n", "\n")
-		// Remove JSON string quotes if present
-		b64Content = strings.Trim(b64Content, "\"")
+		// GitHub API returns base64-encoded content with embedded newlines (line-wrapping every ~76 chars).
+		// The `gh api --jq .content` output is a raw string value (no surrounding quotes).
+		// We strip all whitespace (newlines and spaces) from the base64 string before decoding.
+		b64Content := strings.Map(func(r rune) rune {
+			if r == '\n' || r == '\r' || r == ' ' {
+				return -1 // remove character
+			}
+			return r
+		}, strings.TrimSpace(string(output)))
 		decoded, decErr := base64.StdEncoding.DecodeString(b64Content)
 		if decErr != nil {
 			safeOutputActionsLog.Printf("Failed to decode content for %s: %v", contentPath, decErr)

From 1b7f9bbf2be8c7e19827d7b777b627fa15173767 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 12:45:11 +0000
Subject: [PATCH 04/17] feat: add env field per action, redact strings with
 sanitizeContent, move requires to top

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .../setup/js/safe_output_action_handler.cjs   |  16 ++-
 actions/setup/js/safe_output_helpers.cjs      |   7 +-
 go.mod                                        |   2 +-
 pkg/workflow/safe_outputs_actions.go          |  26 ++++-
 pkg/workflow/safe_outputs_actions_test.go     | 103 ++++++++++++++++++
 5 files changed, 142 insertions(+), 12 deletions(-)

diff --git a/actions/setup/js/safe_output_action_handler.cjs b/actions/setup/js/safe_output_action_handler.cjs
index da002f76fed..3ee73b1f3bd 100644
--- a/actions/setup/js/safe_output_action_handler.cjs
+++ b/actions/setup/js/safe_output_action_handler.cjs
@@ -7,6 +7,7 @@
 
 const { replaceTemporaryIdReferences } = require("./temporary_id.cjs");
 const { getErrorMessage } = require("./error_helpers.cjs");
+const { sanitizeContent } = require("./sanitize_content.cjs");
 
 /**
  * Internal safe-output message fields that should not be forwarded as action inputs.
@@ -69,9 +70,11 @@ async function main(config = {}) {
     try {
       core.info(`Processing custom action: ${actionName}`);
 
-      // Build the processed payload by applying temporary ID substitutions to all
-      // string-valued fields. Non-string fields (numbers, booleans, objects, arrays)
-      // are passed through as-is.
+      // Build the processed payload by:
+      // 1. Applying temporary ID reference substitutions to string fields
+      // 2. Redacting (sanitizing) all string fields via sanitizeContent() before export.
+      //    This prevents prompt-injected content from leaking URLs, mentions, or harmful
+      //    content into external action inputs.
       const processedInputs = {};
       for (const [key, value] of Object.entries(message)) {
         // Skip internal safe-output messaging fields that are not action inputs.
@@ -82,8 +85,11 @@ async function main(config = {}) {
         }
 
         if (typeof value === "string") {
-          // Apply temporary ID reference substitution (e.g., "aw_abc1" → "42")
-          processedInputs[key] = replaceTemporaryIdReferences(value, temporaryIdMap);
+          // Apply temporary ID reference substitution (e.g., "aw_abc1" → "42"), then
+          // sanitize to redact malicious URLs, neutralize bot-trigger phrases, and
+          // escape @mentions that could cause unintended notifications in the action.
+          const substituted = replaceTemporaryIdReferences(value, temporaryIdMap);
+          processedInputs[key] = sanitizeContent(substituted);
         } else {
           processedInputs[key] = value;
         }
diff --git a/actions/setup/js/safe_output_helpers.cjs b/actions/setup/js/safe_output_helpers.cjs
index d178fb75c36..47967926ee8 100644
--- a/actions/setup/js/safe_output_helpers.cjs
+++ b/actions/setup/js/safe_output_helpers.cjs
@@ -6,6 +6,9 @@
  * Provides common validation and target resolution logic
  */
 
+const { getErrorMessage } = require("./error_helpers.cjs");
+const { matchesSimpleGlob } = require("./glob_pattern_helpers.cjs");
+
 /**
  * Parse a comma-separated list of allowed items from environment variable
  * @param {string|undefined} envValue - Environment variable value
@@ -250,7 +253,6 @@ function loadCustomSafeOutputJobTypes() {
     return new Set(jobTypes);
   } catch (error) {
     if (typeof core !== "undefined") {
-      const { getErrorMessage } = require("./error_helpers.cjs");
       core.warning(`Failed to parse GH_AW_SAFE_OUTPUT_JOBS: ${getErrorMessage(error)}`);
     }
     return new Set();
@@ -317,7 +319,6 @@ function extractAssignees(message) {
  * @returns {boolean} True if username matches the blocked pattern
  */
 function matchesBlockedPattern(username, pattern) {
-  const { matchesSimpleGlob } = require("./glob_pattern_helpers.cjs");
   return matchesSimpleGlob(username, pattern);
 }
 
@@ -356,7 +357,6 @@ function loadCustomSafeOutputScriptHandlers() {
     return scriptHandlers;
   } catch (error) {
     if (typeof core !== "undefined") {
-      const { getErrorMessage } = require("./error_helpers.cjs");
       core.warning(`Failed to parse GH_AW_SAFE_OUTPUT_SCRIPTS: ${getErrorMessage(error)}`);
     }
     return new Map();
@@ -387,7 +387,6 @@ function loadCustomSafeOutputActionHandlers() {
     return actionHandlers;
   } catch (error) {
     if (typeof core !== "undefined") {
-      const { getErrorMessage } = require("./error_helpers.cjs");
       core.warning(`Failed to parse GH_AW_SAFE_OUTPUT_ACTIONS: ${getErrorMessage(error)}`);
     }
     return new Map();
diff --git a/go.mod b/go.mod
index eebfb2ef108..0972db58ec3 100644
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,7 @@ require (
 	github.com/sourcegraph/conc v0.3.0
 	github.com/spf13/cobra v1.10.2
 	github.com/stretchr/testify v1.11.1
+	go.yaml.in/yaml/v3 v3.0.4
 	golang.org/x/crypto v0.48.0
 	golang.org/x/mod v0.33.0
 	golang.org/x/term v0.40.0
@@ -102,7 +103,6 @@ require (
 	go.opentelemetry.io/otel/metric v1.39.0 // indirect
 	go.opentelemetry.io/otel/trace v1.39.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
-	go.yaml.in/yaml/v3 v3.0.4 // indirect
 	go.yaml.in/yaml/v4 v4.0.0-rc.4 // indirect
 	golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
 	golang.org/x/net v0.51.0 // indirect
diff --git a/pkg/workflow/safe_outputs_actions.go b/pkg/workflow/safe_outputs_actions.go
index f5e245e1d97..090b944959c 100644
--- a/pkg/workflow/safe_outputs_actions.go
+++ b/pkg/workflow/safe_outputs_actions.go
@@ -22,8 +22,9 @@ var safeOutputActionsLog = logger.New("workflow:safe_outputs_actions")
 // Each configured action is resolved at compile time to get its inputs from action.yml,
 // and is mounted as an MCP tool that the AI agent can call once per workflow run.
 type SafeOutputActionConfig struct {
-	Uses        string `yaml:"uses"`
-	Description string `yaml:"description,omitempty"` // optional override of the action's description
+	Uses        string            `yaml:"uses"`
+	Description string            `yaml:"description,omitempty"` // optional override of the action's description
+	Env         map[string]string `yaml:"env,omitempty"`         // additional environment variables for the injected step
 
 	// Computed at compile time (not from frontmatter):
 	ResolvedRef       string                      `yaml:"-"` // Pinned action reference (e.g., "owner/repo@sha # v1")
@@ -83,6 +84,14 @@ func parseActionsConfig(actionsMap map[string]any) map[string]*SafeOutputActionC
 		if description, ok := actionConfigMap["description"].(string); ok {
 			actionConfig.Description = description
 		}
+		if envMap, ok := actionConfigMap["env"].(map[string]any); ok {
+			actionConfig.Env = make(map[string]string, len(envMap))
+			for k, v := range envMap {
+				if vStr, ok := v.(string); ok {
+					actionConfig.Env[k] = vStr
+				}
+			}
+		}
 
 		if actionConfig.Uses == "" {
 			safeOutputActionsLog.Printf("Warning: action %q is missing required 'uses' field, skipping", actionName)
@@ -408,6 +417,19 @@ func (c *Compiler) buildActionSteps(data *WorkflowData) []string {
 		steps = append(steps, fmt.Sprintf("        if: steps.process_safe_outputs.outputs.%s != ''\n", outputKey))
 		steps = append(steps, fmt.Sprintf("        uses: %s\n", actionRef))
 
+		// Build optional env: block for per-action environment variables
+		if len(config.Env) > 0 {
+			steps = append(steps, "        env:\n")
+			envKeys := make([]string, 0, len(config.Env))
+			for k := range config.Env {
+				envKeys = append(envKeys, k)
+			}
+			sort.Strings(envKeys)
+			for _, envKey := range envKeys {
+				steps = append(steps, fmt.Sprintf("          %s: %s\n", envKey, config.Env[envKey]))
+			}
+		}
+
 		// Build the with: block
 		if len(config.Inputs) > 0 {
 			steps = append(steps, "        with:\n")
diff --git a/pkg/workflow/safe_outputs_actions_test.go b/pkg/workflow/safe_outputs_actions_test.go
index 5b09ff75231..7f741643805 100644
--- a/pkg/workflow/safe_outputs_actions_test.go
+++ b/pkg/workflow/safe_outputs_actions_test.go
@@ -415,3 +415,106 @@ func TestActionOutputKey(t *testing.T) {
 	assert.Equal(t, "action_my_tool_payload", actionOutputKey("my_tool"))
 	assert.Equal(t, "action_another_action_payload", actionOutputKey("another_action"))
 }
+
+// TestParseActionsConfigWithEnv verifies parsing of env field in actions config
+func TestParseActionsConfigWithEnv(t *testing.T) {
+	actionsMap := map[string]any{
+		"my-tool": map[string]any{
+			"uses": "owner/repo@v1",
+			"env": map[string]any{
+				"MY_VAR":    "my-value",
+				"OTHER_VAR": "other-value",
+			},
+		},
+	}
+
+	result := parseActionsConfig(actionsMap)
+	require.Len(t, result, 1, "Should have one action")
+
+	myTool := result["my-tool"]
+	require.NotNil(t, myTool, "Should have my-tool")
+	require.Len(t, myTool.Env, 2, "Should have 2 env vars")
+	assert.Equal(t, "my-value", myTool.Env["MY_VAR"], "MY_VAR should match")
+	assert.Equal(t, "other-value", myTool.Env["OTHER_VAR"], "OTHER_VAR should match")
+}
+
+// TestBuildActionStepsWithEnv verifies that env vars are emitted in generated steps
+func TestBuildActionStepsWithEnv(t *testing.T) {
+	compiler := NewCompiler()
+	data := &WorkflowData{
+		SafeOutputs: &SafeOutputsConfig{
+			Actions: map[string]*SafeOutputActionConfig{
+				"my-tool": {
+					Uses:        "owner/repo@v1",
+					ResolvedRef: "owner/repo@abc123 # v1",
+					Env: map[string]string{
+						"MY_SECRET": "${{ secrets.MY_SECRET }}",
+						"MY_VAR":    "static-value",
+					},
+					Inputs: map[string]*ActionYAMLInput{
+						"title": {Required: true},
+					},
+				},
+			},
+		},
+	}
+
+	steps := compiler.buildActionSteps(data)
+	require.NotEmpty(t, steps, "Should generate steps")
+
+	fullYAML := strings.Join(steps, "")
+	assert.Contains(t, fullYAML, "env:", "Should have env block")
+	assert.Contains(t, fullYAML, "MY_SECRET: ${{ secrets.MY_SECRET }}", "Should have MY_SECRET env var")
+	assert.Contains(t, fullYAML, "MY_VAR: static-value", "Should have MY_VAR env var")
+	// Env block should appear before with: block
+	envIdx := strings.Index(fullYAML, "env:")
+	withIdx := strings.Index(fullYAML, "with:")
+	assert.Less(t, envIdx, withIdx, "env: should appear before with:")
+}
+
+// TestBuildActionStepsNoEnv verifies that no env block is emitted when Env is empty
+func TestBuildActionStepsNoEnv(t *testing.T) {
+	compiler := NewCompiler()
+	data := &WorkflowData{
+		SafeOutputs: &SafeOutputsConfig{
+			Actions: map[string]*SafeOutputActionConfig{
+				"my-tool": {
+					Uses:        "owner/repo@v1",
+					ResolvedRef: "owner/repo@abc123 # v1",
+					Inputs: map[string]*ActionYAMLInput{
+						"title": {Required: true},
+					},
+				},
+			},
+		},
+	}
+
+	steps := compiler.buildActionSteps(data)
+	fullYAML := strings.Join(steps, "")
+	assert.NotContains(t, fullYAML, "env:", "Should not have env block when Env is nil/empty")
+}
+
+// TestExtractSafeOutputsConfigIncludesActionsWithEnv verifies env is parsed via extractSafeOutputsConfig
+func TestExtractSafeOutputsConfigIncludesActionsWithEnv(t *testing.T) {
+	compiler := NewCompiler()
+	frontmatter := map[string]any{
+		"safe-outputs": map[string]any{
+			"actions": map[string]any{
+				"my-action": map[string]any{
+					"uses": "owner/repo@v1",
+					"env": map[string]any{
+						"TOKEN": "${{ secrets.MY_TOKEN }}",
+					},
+				},
+			},
+		},
+	}
+
+	config := compiler.extractSafeOutputsConfig(frontmatter)
+
+	require.NotNil(t, config, "Should extract config")
+	action := config.Actions["my-action"]
+	require.NotNil(t, action, "Should have my-action")
+	require.Len(t, action.Env, 1, "Should have 1 env var")
+	assert.Equal(t, "${{ secrets.MY_TOKEN }}", action.Env["TOKEN"], "TOKEN env var should match")
+}

From 44a547d4c17d161af1eed4d1bcf19b18231b8ac7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 13:24:14 +0000
Subject: [PATCH 05/17] feat: add add-smoked-label action to smoke-codex, skip
 ${{ inputs from with: block

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .github/workflows/smoke-codex.lock.yml       | 23 +++++-
 .github/workflows/smoke-codex.md             |  7 ++
 pkg/parser/schemas/main_workflow_schema.json | 31 ++++++++
 pkg/workflow/safe_outputs_actions.go         | 41 ++++++++---
 pkg/workflow/safe_outputs_actions_test.go    | 74 ++++++++++++++++++++
 5 files changed, 165 insertions(+), 11 deletions(-)

diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index e1ae4d497a1..0e37c3d2db6 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -27,7 +27,7 @@
 #     - shared/gh.md
 #     - shared/reporting.md
 #
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"57064b51acff99a53ad08aada3ddf9626cdf25a44c87298d5df01726d62273e5","strict":true}
+# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"f0c83e64ff01b5e650c92aaaf519cf17c35ab121d84d633cf4da7cb48488592c","strict":true}
 
 name: "Smoke Codex"
 "on":
@@ -446,7 +446,17 @@ jobs:
               "remove_labels": " CONSTRAINTS: Only these labels can be removed: [smoke]."
             },
             "repo_params": {},
-            "dynamic_tools": []
+            "dynamic_tools": [
+              {
+                "description": "Add the 'smoked' label to the current pull request (can only be called once)",
+                "inputSchema": {
+                  "additionalProperties": false,
+                  "properties": {},
+                  "type": "object"
+                },
+                "name": "add_smoked_label"
+              }
+            ]
           }
           GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF
           cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
@@ -1530,6 +1540,7 @@ jobs:
           GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,127.0.0.1,172.30.0.1,::1,api.openai.com,api.snapcraft.io,app.renovatebot.com,appveyor.com,archive.ubuntu.com,azure.archive.ubuntu.com,badgen.net,cdn.playwright.dev,circleci.com,codacy.com,codeclimate.com,codecov.io,codeload.github.com,coveralls.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deepsource.io,docs.github.com,drone.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,img.shields.io,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,localhost,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,readthedocs.io,readthedocs.org,renovatebot.com,s.symcb.com,s.symcd.com,security.ubuntu.com,semaphoreci.com,shields.io,snyk.io,sonarcloud.io,sonarqube.com,storage.googleapis.com,sum.golang.org,travis-ci.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
+          GH_AW_SAFE_OUTPUT_ACTIONS: "{\"add_smoked_label\":\"add_smoked_label\"}"
           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,\"labels\":[\"automation\",\"testing\"],\"max\":1},\"hide_comment\":{\"max\":5},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"remove_labels\":{\"allowed\":[\"smoke\"]},\"unassign_from_user\":{\"allowed\":[\"githubactionagent\"],\"max\":1}}"
         with:
           github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
@@ -1538,6 +1549,14 @@ jobs:
             setupGlobals(core, github, context, exec, io);
             const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs');
             await main();
+      - name: Add the 'smoked' label to the current pull request
+        id: action_add_smoked_label
+        if: steps.process_safe_outputs.outputs.action_add_smoked_label_payload != ''
+        uses: actions-ecosystem/action-add-labels@v1
+        env:
+          GITHUB_TOKEN: ${{ github.token }}
+        with:
+          payload: ${{ steps.process_safe_outputs.outputs.action_add_smoked_label_payload }}
       - name: Upload Safe Output Items Manifest
         if: always()
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
diff --git a/.github/workflows/smoke-codex.md b/.github/workflows/smoke-codex.md
index 2b8f27305b2..cd0316b1153 100644
--- a/.github/workflows/smoke-codex.md
+++ b/.github/workflows/smoke-codex.md
@@ -62,6 +62,12 @@ safe-outputs:
       run-started: "🔮 The ancient spirits stir... [{workflow_name}]({run_url}) awakens to divine this {event_type}..."
       run-success: "✨ The prophecy is fulfilled... [{workflow_name}]({run_url}) has completed its mystical journey. The stars align. 🌟"
       run-failure: "🌑 The shadows whisper... [{workflow_name}]({run_url}) {status}. The oracle requires further meditation..."
+    actions:
+      add-smoked-label:
+        uses: actions-ecosystem/action-add-labels@v1
+        description: Add the 'smoked' label to the current pull request
+        env:
+          GITHUB_TOKEN: ${{ github.token }}
 timeout-minutes: 15
 checkout:
   - fetch-depth: 2
@@ -99,6 +105,7 @@ If all tests pass:
 - Use the `add_labels` safe-output tool to add the label `smoke-codex` to the pull request
 - Use the `remove_labels` safe-output tool to remove the label `smoke` from the pull request
 - Use the `unassign_from_user` safe-output tool to unassign the user `githubactionagent` from the pull request (this is a fictitious user used for testing)
+- Use the `add_smoked_label` safe-output action tool to add the label `smoked` to the pull request (call it with `{"labels": "smoked"}`)
 
 **Important**: If no action is needed after completing your analysis, you **MUST** call the `noop` safe-output tool with a brief explanation. Failing to call any safe-output tool is the most common cause of safe-output workflow failures.
 
diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json
index d0e54ea6bbf..c78d67d7a76 100644
--- a/pkg/parser/schemas/main_workflow_schema.json
+++ b/pkg/parser/schemas/main_workflow_schema.json
@@ -7829,6 +7829,37 @@
               }
             ]
           ]
+        },
+        "actions": {
+          "type": "object",
+          "description": "Custom GitHub Actions to mount as once-callable MCP tools. Each action is resolved at compile time to derive its input schema from action.yml, and a guarded `uses:` step is injected in the safe_outputs job. Action names containing dashes will be automatically normalized to underscores (e.g., 'add-smoked-label' becomes 'add_smoked_label').",
+          "patternProperties": {
+            "^[a-zA-Z_][a-zA-Z0-9_-]*$": {
+              "type": "object",
+              "description": "Configuration for a custom safe output action.",
+              "properties": {
+                "uses": {
+                  "type": "string",
+                  "description": "The GitHub Action to use. Supports owner/repo@ref, owner/repo/subdir@ref, or ./local/path.",
+                  "examples": ["actions-ecosystem/action-add-labels@v1", "owner/repo@v1", "owner/repo/subdir@v1"]
+                },
+                "description": {
+                  "type": "string",
+                  "description": "Optional description override for the MCP tool. Defaults to the action's description from action.yml."
+                },
+                "env": {
+                  "type": "object",
+                  "description": "Additional environment variables to set on the injected action step. Useful for passing secrets like GITHUB_TOKEN.",
+                  "additionalProperties": {
+                    "type": "string"
+                  },
+                  "examples": [{ "GITHUB_TOKEN": "${{ github.token }}" }]
+                }
+              },
+              "required": ["uses"],
+              "additionalProperties": false
+            }
+          }
         }
       },
       "additionalProperties": false
diff --git a/pkg/workflow/safe_outputs_actions.go b/pkg/workflow/safe_outputs_actions.go
index 090b944959c..24ea9175789 100644
--- a/pkg/workflow/safe_outputs_actions.go
+++ b/pkg/workflow/safe_outputs_actions.go
@@ -269,6 +269,18 @@ func parseActionYAMLContent(content []byte) (*actionYAMLFile, error) {
 	return &parsed, nil
 }
 
+// isGitHubExpressionDefault returns true if the input's default value is a GitHub Actions
+// expression (e.g., "${{ github.token }}" or "${{ github.event.pull_request.number }}").
+// Such inputs should not be included in the MCP tool schema or the generated `with:` block
+// so that GitHub Actions can apply the defaults naturally rather than having them overridden
+// with an empty string from a missing JSON key in the agent payload.
+func isGitHubExpressionDefault(input *ActionYAMLInput) bool {
+	if input == nil {
+		return false
+	}
+	return strings.HasPrefix(strings.TrimSpace(input.Default), "${{")
+}
+
 // generateActionToolDefinition creates an MCP tool definition for a custom safe output action.
 // The tool name is the normalized action name. Inputs are derived from the action.yml.
 func generateActionToolDefinition(actionName string, config *SafeOutputActionConfig) map[string]any {
@@ -303,6 +315,12 @@ func generateActionToolDefinition(actionName string, config *SafeOutputActionCon
 
 		for _, inputName := range inputNames {
 			inputDef := config.Inputs[inputName]
+			// Skip inputs whose defaults are GitHub expression (e.g. "${{ github.token }}").
+			// These are implementation details (authentication, context values) that the agent
+			// should not provide — GitHub Actions will apply the defaults automatically.
+			if isGitHubExpressionDefault(inputDef) {
+				continue
+			}
 			property := map[string]any{
 				"type": "string",
 			}
@@ -432,17 +450,22 @@ func (c *Compiler) buildActionSteps(data *WorkflowData) []string {
 
 		// Build the with: block
 		if len(config.Inputs) > 0 {
-			steps = append(steps, "        with:\n")
-
-			inputNames := make([]string, 0, len(config.Inputs))
-			for k := range config.Inputs {
-				inputNames = append(inputNames, k)
+			// Filter to only inputs that the agent should provide (exclude those with GitHub
+			// expression defaults like "${{ github.token }}" — GitHub Actions applies them naturally).
+			agentInputNames := make([]string, 0, len(config.Inputs))
+			for k, v := range config.Inputs {
+				if !isGitHubExpressionDefault(v) {
+					agentInputNames = append(agentInputNames, k)
+				}
 			}
-			sort.Strings(inputNames)
+			sort.Strings(agentInputNames)
 
-			for _, inputName := range inputNames {
-				steps = append(steps, fmt.Sprintf("          %s: ${{ fromJSON(steps.process_safe_outputs.outputs.%s).%s }}\n",
-					inputName, outputKey, inputName))
+			if len(agentInputNames) > 0 {
+				steps = append(steps, "        with:\n")
+				for _, inputName := range agentInputNames {
+					steps = append(steps, fmt.Sprintf("          %s: ${{ fromJSON(steps.process_safe_outputs.outputs.%s).%s }}\n",
+						inputName, outputKey, inputName))
+				}
 			}
 		} else {
 			// When inputs couldn't be resolved, pass the raw payload as a single input
diff --git a/pkg/workflow/safe_outputs_actions_test.go b/pkg/workflow/safe_outputs_actions_test.go
index 7f741643805..c92f2c5d04e 100644
--- a/pkg/workflow/safe_outputs_actions_test.go
+++ b/pkg/workflow/safe_outputs_actions_test.go
@@ -518,3 +518,77 @@ func TestExtractSafeOutputsConfigIncludesActionsWithEnv(t *testing.T) {
 	require.Len(t, action.Env, 1, "Should have 1 env var")
 	assert.Equal(t, "${{ secrets.MY_TOKEN }}", action.Env["TOKEN"], "TOKEN env var should match")
 }
+
+// TestIsGitHubExpressionDefault verifies detection of GitHub expression defaults
+func TestIsGitHubExpressionDefault(t *testing.T) {
+	tests := []struct {
+		name     string
+		input    *ActionYAMLInput
+		expected bool
+	}{
+		{"nil input", nil, false},
+		{"no default", &ActionYAMLInput{}, false},
+		{"static default", &ActionYAMLInput{Default: "latest"}, false},
+		{"github token expression", &ActionYAMLInput{Default: "${{ github.token }}"}, true},
+		{"pr number expression", &ActionYAMLInput{Default: "${{ github.event.pull_request.number }}"}, true},
+		{"expression with leading whitespace", &ActionYAMLInput{Default: "  ${{ github.token }}"}, true},
+		{"partial expression no closing", &ActionYAMLInput{Default: "${{ incomplete"}, true}, // starts with ${{
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			assert.Equal(t, tt.expected, isGitHubExpressionDefault(tt.input), "isGitHubExpressionDefault result should match")
+		})
+	}
+}
+
+// TestBuildActionStepsSkipsGitHubExpressionDefaultInputs verifies inputs with ${{ defaults are excluded
+func TestBuildActionStepsSkipsGitHubExpressionDefaultInputs(t *testing.T) {
+	compiler := NewCompiler()
+	data := &WorkflowData{
+		SafeOutputs: &SafeOutputsConfig{
+			Actions: map[string]*SafeOutputActionConfig{
+				"add-labels": {
+					Uses:        "actions-ecosystem/action-add-labels@v1",
+					ResolvedRef: "actions-ecosystem/action-add-labels@abc123 # v1",
+					Inputs: map[string]*ActionYAMLInput{
+						"github_token": {Required: true, Default: "${{ github.token }}"},
+						"number":       {Required: false, Default: "${{ github.event.pull_request.number }}"},
+						"labels":       {Required: true},
+					},
+				},
+			},
+		},
+	}
+
+	steps := compiler.buildActionSteps(data)
+	fullYAML := strings.Join(steps, "")
+
+	// Only 'labels' should be in with: block
+	assert.Contains(t, fullYAML, "labels:", "labels should be in with: block")
+	assert.NotContains(t, fullYAML, "github_token:", "github_token should be excluded (has ${{ default})")
+	assert.NotContains(t, fullYAML, "number:", "number should be excluded (has ${{ default})")
+}
+
+// TestGenerateActionToolDefinitionSkipsGitHubExpressionInputs verifies schema omits ${{ inputs
+func TestGenerateActionToolDefinitionSkipsGitHubExpressionInputs(t *testing.T) {
+	config := &SafeOutputActionConfig{
+		Uses: "actions-ecosystem/action-add-labels@v1",
+		Inputs: map[string]*ActionYAMLInput{
+			"github_token": {Required: true, Default: "${{ github.token }}"},
+			"number":       {Required: false, Default: "${{ github.event.pull_request.number }}"},
+			"labels":       {Required: true, Description: "Labels to add"},
+		},
+	}
+
+	tool := generateActionToolDefinition("add-labels", config)
+
+	schema, ok := tool["inputSchema"].(map[string]any)
+	require.True(t, ok, "inputSchema should be a map")
+	properties, ok := schema["properties"].(map[string]any)
+	require.True(t, ok, "properties should be a map")
+
+	assert.Contains(t, properties, "labels", "labels should be in schema")
+	assert.NotContains(t, properties, "github_token", "github_token should be excluded from schema")
+	assert.NotContains(t, properties, "number", "number should be excluded from schema")
+}

From 84df56f8d0f0aef406f361af7e6dceb38bf27f06 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 14:18:44 +0000
Subject: [PATCH 06/17] fix: address code review - resolve actions early, use
 pinned SHA for fetch, fix fallback schema

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .github/aw/actions-lock.json              |   5 +
 .github/workflows/smoke-claude.lock.yml   |   2 +
 .github/workflows/smoke-codex.lock.yml    |  41 +++++----
 pkg/workflow/compiler_jobs.go             |   8 ++
 pkg/workflow/compiler_safe_outputs_job.go |   4 +-
 pkg/workflow/data/action_pins.json        |   5 +
 pkg/workflow/safe_outputs_actions.go      | 107 ++++++++++++++++------
 pkg/workflow/safe_outputs_actions_test.go |  47 +++++++++-
 8 files changed, 168 insertions(+), 51 deletions(-)

diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json
index 6ca9158024a..743652c9461 100644
--- a/.github/aw/actions-lock.json
+++ b/.github/aw/actions-lock.json
@@ -1,5 +1,10 @@
 {
   "entries": {
+    "actions-ecosystem/action-add-labels@v1": {
+      "repo": "actions-ecosystem/action-add-labels",
+      "version": "v1",
+      "sha": "18f1af5e3544586314bbe15c0273249c770b2daf"
+    },
     "actions/ai-inference@v2.0.7": {
       "repo": "actions/ai-inference",
       "version": "v2.0.7",
diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml
index b0d7a0ad13c..c9f93bb7622 100644
--- a/.github/workflows/smoke-claude.lock.yml
+++ b/.github/workflows/smoke-claude.lock.yml
@@ -2629,6 +2629,8 @@ jobs:
           /// <reference types="./safe-output-script" />
           // Auto-generated safe-output script handler: post-slack-message
           
+          const { sanitizeContent } = require("./sanitize_content.cjs");
+          
           /** @type {import('./types/safe-output-script').SafeOutputScriptMain} */
           async function main(config = {}) {
             const { channel, message } = config;
diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index 0e37c3d2db6..ba99018cad2 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -107,6 +107,19 @@ jobs:
             setupGlobals(core, github, context, exec, io);
             const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
             await main(core, context);
+      - name: Add hooray reaction for immediate feedback
+        id: react
+        if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || (github.event_name == 'pull_request') && (github.event.pull_request.head.repo.id == github.repository_id)
+        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+        env:
+          GH_AW_REACTION: "hooray"
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          script: |
+            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+            setupGlobals(core, github, context, exec, io);
+            const { main } = require('${{ runner.temp }}/gh-aw/actions/add_reaction.cjs');
+            await main();
       - name: Validate CODEX_API_KEY or OPENAI_API_KEY secret
         id: validate-secret
         run: ${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh CODEX_API_KEY OPENAI_API_KEY Codex https://github.github.com/gh-aw/reference/engines/#openai-codex
@@ -122,19 +135,6 @@ jobs:
             .agents
           sparse-checkout-cone-mode: true
           fetch-depth: 1
-      - name: Add hooray reaction for immediate feedback
-        id: react
-        if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || (github.event_name == 'pull_request') && (github.event.pull_request.head.repo.id == github.repository_id)
-        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
-        env:
-          GH_AW_REACTION: "hooray"
-        with:
-          github-token: ${{ secrets.GITHUB_TOKEN }}
-          script: |
-            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
-            setupGlobals(core, github, context, exec, io);
-            const { main } = require('${{ runner.temp }}/gh-aw/actions/add_reaction.cjs');
-            await main();
       - name: Check workflow file timestamps
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
         env:
@@ -411,7 +411,7 @@ jobs:
         with:
           node-version: '24'
           package-manager-cache: false
-      - name: Install Codex
+      - name: Install Codex CLI
         run: npm install -g @openai/codex@latest
       - name: Install AWF binary
         run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.24.3
@@ -450,8 +450,13 @@ jobs:
               {
                 "description": "Add the 'smoked' label to the current pull request (can only be called once)",
                 "inputSchema": {
-                  "additionalProperties": false,
-                  "properties": {},
+                  "additionalProperties": true,
+                  "properties": {
+                    "payload": {
+                      "description": "JSON-encoded payload to pass to the action",
+                      "type": "string"
+                    }
+                  },
                   "type": "object"
                 },
                 "name": "add_smoked_label"
@@ -1552,12 +1557,12 @@ jobs:
       - name: Add the 'smoked' label to the current pull request
         id: action_add_smoked_label
         if: steps.process_safe_outputs.outputs.action_add_smoked_label_payload != ''
-        uses: actions-ecosystem/action-add-labels@v1
+        uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf # v1
         env:
           GITHUB_TOKEN: ${{ github.token }}
         with:
           payload: ${{ steps.process_safe_outputs.outputs.action_add_smoked_label_payload }}
-      - name: Upload Safe Output Items Manifest
+      - name: Upload Safe Outputs Items Manifest
         if: always()
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go
index 88a35cb1f7e..160d0285b2f 100644
--- a/pkg/workflow/compiler_jobs.go
+++ b/pkg/workflow/compiler_jobs.go
@@ -198,6 +198,14 @@ func (c *Compiler) buildJobs(data *WorkflowData, markdownPath string) error {
 	// Extract lock filename for timestamp check
 	lockFilename := filepath.Base(stringutil.MarkdownToLockFile(markdownPath))
 
+	// Resolve custom safe-output actions early so that tool schemas (derived from action.yml)
+	// are available when buildMainJobWrapper → generateMCPSetup → generateToolsMetaJSON →
+	// generateDynamicTools runs. Without this early resolution the dynamic_tools entry for
+	// each action tool would have an empty schema because Inputs/ActionDescription are nil.
+	if data.SafeOutputs != nil && len(data.SafeOutputs.Actions) > 0 {
+		c.resolveAllActions(data, markdownPath)
+	}
+
 	// Build pre-activation and activation jobs
 	_, activationJobCreated, err := c.buildPreActivationAndActivationJobs(data, frontmatter, lockFilename)
 	if err != nil {
diff --git a/pkg/workflow/compiler_safe_outputs_job.go b/pkg/workflow/compiler_safe_outputs_job.go
index 8ebc5637962..36e82de6f29 100644
--- a/pkg/workflow/compiler_safe_outputs_job.go
+++ b/pkg/workflow/compiler_safe_outputs_job.go
@@ -228,7 +228,9 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa
 	// a JSON payload output for each action tool call. Each step is guarded by an `if:` condition
 	// that checks whether the handler manager exported a payload for this action.
 	if len(data.SafeOutputs.Actions) > 0 {
-		// Resolve action.yml for all actions (fetches inputs/descriptions at compile time)
+		// resolveAllActions was already called early in buildJobs (before generateToolsMetaJSON)
+		// so action configs already have Inputs/ActionDescription populated. We only call it
+		// again here as a safety net in case compileSafeOutputsJob is called independently.
 		c.resolveAllActions(data, markdownPath)
 
 		actionStepYAML := c.buildActionSteps(data)
diff --git a/pkg/workflow/data/action_pins.json b/pkg/workflow/data/action_pins.json
index 6ca9158024a..743652c9461 100644
--- a/pkg/workflow/data/action_pins.json
+++ b/pkg/workflow/data/action_pins.json
@@ -1,5 +1,10 @@
 {
   "entries": {
+    "actions-ecosystem/action-add-labels@v1": {
+      "repo": "actions-ecosystem/action-add-labels",
+      "version": "v1",
+      "sha": "18f1af5e3544586314bbe15c0273249c770b2daf"
+    },
     "actions/ai-inference@v2.0.7": {
       "repo": "actions/ai-inference",
       "version": "v2.0.7",
diff --git a/pkg/workflow/safe_outputs_actions.go b/pkg/workflow/safe_outputs_actions.go
index 24ea9175789..eb6b1aecabb 100644
--- a/pkg/workflow/safe_outputs_actions.go
+++ b/pkg/workflow/safe_outputs_actions.go
@@ -168,15 +168,24 @@ func (c *Compiler) fetchAndParseActionYAML(actionName string, config *SafeOutput
 	} else {
 		// Pin the action ref and fetch the action.yml
 		pinned, pinErr := GetActionPinWithData(ref.Repo, ref.Ref, data)
+		var fetchRef string
 		if pinErr != nil {
 			safeOutputActionsLog.Printf("Warning: failed to pin action %q (%s@%s): %v", actionName, ref.Repo, ref.Ref, pinErr)
 			// Fall back to using the original ref
 			resolvedRef = config.Uses
+			fetchRef = ref.Ref
 		} else {
 			resolvedRef = pinned
+			// Extract the pinned SHA from the reference (format: "repo@sha # tag")
+			// and use it to fetch action.yml so the schema matches the exact pinned version.
+			if sha := extractSHAFromPinnedRef(pinned); sha != "" {
+				fetchRef = sha
+			} else {
+				fetchRef = ref.Ref
+			}
 		}
 
-		actionYAML, err = fetchRemoteActionYAML(ref.Repo, ref.Subdir, ref.Ref)
+		actionYAML, err = fetchRemoteActionYAML(ref.Repo, ref.Subdir, fetchRef)
 		if err != nil {
 			safeOutputActionsLog.Printf("Warning: failed to fetch action.yml for %q (%s): %v", actionName, config.Uses, err)
 		}
@@ -192,6 +201,27 @@ func (c *Compiler) fetchAndParseActionYAML(actionName string, config *SafeOutput
 
 // fetchRemoteActionYAML fetches and parses action.yml from a GitHub repository.
 // It tries both action.yml and action.yaml filenames.
+
+// extractSHAFromPinnedRef parses the SHA from a pinned action reference string.
+// The format produced by formatActionReference is "repo@sha # version".
+// Returns the SHA string, or empty string if it cannot be parsed.
+func extractSHAFromPinnedRef(pinned string) string {
+	// Find the @ separator between repo and sha
+	_, afterAt, found := strings.Cut(pinned, "@")
+	if !found {
+		return ""
+	}
+	// Strip the "# version" comment
+	if commentIdx := strings.Index(afterAt, " #"); commentIdx >= 0 {
+		afterAt = strings.TrimSpace(afterAt[:commentIdx])
+	}
+	// Validate it looks like a full SHA (40 hex chars)
+	if isValidFullSHA(afterAt) {
+		return afterAt
+	}
+	return ""
+}
+
 func fetchRemoteActionYAML(repo, subdir, ref string) (*actionYAMLFile, error) {
 	for _, filename := range []string{"action.yml", "action.yaml"} {
 		var contentPath string
@@ -296,6 +326,27 @@ func generateActionToolDefinition(actionName string, config *SafeOutputActionCon
 	// Append once-only constraint to description
 	description += " (can only be called once)"
 
+	// When action.yml could not be fetched at compile time (Inputs == nil), generate a
+	// permissive fallback schema so the agent can still call the tool. The runtime step
+	// passes the raw payload through a single `payload` input rather than individual fields.
+	if config.Inputs == nil {
+		inputSchema := map[string]any{
+			"type": "object",
+			"properties": map[string]any{
+				"payload": map[string]any{
+					"type":        "string",
+					"description": "JSON-encoded payload to pass to the action",
+				},
+			},
+			"additionalProperties": true,
+		}
+		return map[string]any{
+			"name":        normalizedName,
+			"description": description,
+			"inputSchema": inputSchema,
+		}
+	}
+
 	inputSchema := map[string]any{
 		"type":                 "object",
 		"properties":           make(map[string]any),
@@ -305,36 +356,34 @@ func generateActionToolDefinition(actionName string, config *SafeOutputActionCon
 	var requiredFields []string
 	properties := inputSchema["properties"].(map[string]any)
 
-	if config.Inputs != nil {
-		// Sort for deterministic output
-		inputNames := make([]string, 0, len(config.Inputs))
-		for k := range config.Inputs {
-			inputNames = append(inputNames, k)
+	// Sort for deterministic output
+	inputNames := make([]string, 0, len(config.Inputs))
+	for k := range config.Inputs {
+		inputNames = append(inputNames, k)
+	}
+	sort.Strings(inputNames)
+
+	for _, inputName := range inputNames {
+		inputDef := config.Inputs[inputName]
+		// Skip inputs whose defaults are GitHub expression (e.g. "${{ github.token }}").
+		// These are implementation details (authentication, context values) that the agent
+		// should not provide — GitHub Actions will apply the defaults automatically.
+		if isGitHubExpressionDefault(inputDef) {
+			continue
 		}
-		sort.Strings(inputNames)
-
-		for _, inputName := range inputNames {
-			inputDef := config.Inputs[inputName]
-			// Skip inputs whose defaults are GitHub expression (e.g. "${{ github.token }}").
-			// These are implementation details (authentication, context values) that the agent
-			// should not provide — GitHub Actions will apply the defaults automatically.
-			if isGitHubExpressionDefault(inputDef) {
-				continue
-			}
-			property := map[string]any{
-				"type": "string",
-			}
-			if inputDef.Description != "" {
-				property["description"] = inputDef.Description
-			}
-			if inputDef.Default != "" {
-				property["default"] = inputDef.Default
-			}
-			if inputDef.Required {
-				requiredFields = append(requiredFields, inputName)
-			}
-			properties[inputName] = property
+		property := map[string]any{
+			"type": "string",
+		}
+		if inputDef.Description != "" {
+			property["description"] = inputDef.Description
+		}
+		if inputDef.Default != "" {
+			property["default"] = inputDef.Default
+		}
+		if inputDef.Required {
+			requiredFields = append(requiredFields, inputName)
 		}
+		properties[inputName] = property
 	}
 
 	if len(requiredFields) > 0 {
diff --git a/pkg/workflow/safe_outputs_actions_test.go b/pkg/workflow/safe_outputs_actions_test.go
index c92f2c5d04e..e9687edcb1f 100644
--- a/pkg/workflow/safe_outputs_actions_test.go
+++ b/pkg/workflow/safe_outputs_actions_test.go
@@ -222,20 +222,24 @@ func TestGenerateActionToolDefinitionFallbackDescription(t *testing.T) {
 	}
 }
 
-// TestGenerateActionToolDefinitionNoInputs verifies tool with no inputs (resolution failed)
+// TestGenerateActionToolDefinitionNoInputs verifies the fallback schema when resolution failed (Inputs == nil)
 func TestGenerateActionToolDefinitionNoInputs(t *testing.T) {
 	config := &SafeOutputActionConfig{
 		Uses: "owner/repo@v1",
+		// Inputs is nil (action.yml resolution failed)
 	}
 
 	tool := generateActionToolDefinition("my-tool", config)
 
 	schema, ok := tool["inputSchema"].(map[string]any)
 	require.True(t, ok, "inputSchema should be a map")
+	// Fallback schema should be permissive (additionalProperties: true) so agent can still call the tool
+	assert.Equal(t, true, schema["additionalProperties"], "Fallback schema should allow additional properties")
+	// Should have a payload property to hint to the agent what to send
 	properties, ok := schema["properties"].(map[string]any)
 	require.True(t, ok, "properties should be a map")
-	assert.Empty(t, properties, "No properties when inputs unknown")
-	assert.Nil(t, schema["required"], "No required fields when inputs unknown")
+	assert.Contains(t, properties, "payload", "Fallback schema should include a 'payload' property")
+	assert.Nil(t, schema["required"], "No required fields in fallback schema")
 }
 
 // TestBuildCustomSafeOutputActionsJSON verifies JSON generation for GH_AW_SAFE_OUTPUT_ACTIONS
@@ -592,3 +596,40 @@ func TestGenerateActionToolDefinitionSkipsGitHubExpressionInputs(t *testing.T) {
 	assert.NotContains(t, properties, "github_token", "github_token should be excluded from schema")
 	assert.NotContains(t, properties, "number", "number should be excluded from schema")
 }
+
+// TestExtractSHAFromPinnedRef verifies SHA extraction from pinned action references
+func TestExtractSHAFromPinnedRef(t *testing.T) {
+	tests := []struct {
+		name     string
+		pinned   string
+		expected string
+	}{
+		{
+			name:     "standard pinned reference",
+			pinned:   "actions-ecosystem/action-add-labels@" + strings.Repeat("a", 40) + " # v1",
+			expected: strings.Repeat("a", 40),
+		},
+		{
+			name:     "no @ separator",
+			pinned:   "no-at-sign",
+			expected: "",
+		},
+		{
+			name:     "non-SHA after @",
+			pinned:   "owner/repo@v1 # v1",
+			expected: "",
+		},
+		{
+			name:     "empty string",
+			pinned:   "",
+			expected: "",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			result := extractSHAFromPinnedRef(tt.pinned)
+			assert.Equal(t, tt.expected, result, "extractSHAFromPinnedRef result should match")
+		})
+	}
+}

From 398b143c8320e9194e7e5906ee5eb7a76a555b44 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 14:41:12 +0000
Subject: [PATCH 07/17] fix: update MessageHandlerFunction type to accept
 optional temporaryIdMap parameter

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .github/workflows/smoke-codex.lock.yml          | 16 ++++++++++++----
 actions/setup/js/safe_output_action_handler.cjs |  2 +-
 actions/setup/js/types/handler-factory.d.ts     |  3 ++-
 3 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index ba99018cad2..a4060f236fa 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -450,13 +450,20 @@ jobs:
               {
                 "description": "Add the 'smoked' label to the current pull request (can only be called once)",
                 "inputSchema": {
-                  "additionalProperties": true,
+                  "additionalProperties": false,
                   "properties": {
-                    "payload": {
-                      "description": "JSON-encoded payload to pass to the action",
+                    "labels": {
+                      "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.",
+                      "type": "string"
+                    },
+                    "number": {
+                      "description": "The number of the issue or pull request.",
                       "type": "string"
                     }
                   },
+                  "required": [
+                    "labels"
+                  ],
                   "type": "object"
                 },
                 "name": "add_smoked_label"
@@ -1561,7 +1568,8 @@ jobs:
         env:
           GITHUB_TOKEN: ${{ github.token }}
         with:
-          payload: ${{ steps.process_safe_outputs.outputs.action_add_smoked_label_payload }}
+          labels: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).labels }}
+          number: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).number }}
       - name: Upload Safe Outputs Items Manifest
         if: always()
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
diff --git a/actions/setup/js/safe_output_action_handler.cjs b/actions/setup/js/safe_output_action_handler.cjs
index 3ee73b1f3bd..3359e685e6f 100644
--- a/actions/setup/js/safe_output_action_handler.cjs
+++ b/actions/setup/js/safe_output_action_handler.cjs
@@ -55,7 +55,7 @@ async function main(config = {}) {
    * @param {Map<string, Object>} temporaryIdMap - Live map of temporary IDs (for substitution)
    * @returns {Promise<Object>} Result with success/error status
    */
-  return async function handleCustomAction(message, resolvedTemporaryIds, temporaryIdMap) {
+  return async function handleCustomAction(message, resolvedTemporaryIds, temporaryIdMap = new Map()) {
     // Enforce once-only constraint
     if (called) {
       const error = `Action "${actionName}" can only be called once per workflow run`;
diff --git a/actions/setup/js/types/handler-factory.d.ts b/actions/setup/js/types/handler-factory.d.ts
index b40b336a270..848817b2a20 100644
--- a/actions/setup/js/types/handler-factory.d.ts
+++ b/actions/setup/js/types/handler-factory.d.ts
@@ -67,9 +67,10 @@ type HandlerResult = HandlerSuccessResult | HandlerErrorResult;
  *
  * @param message - The safe output message to process
  * @param resolvedTemporaryIds - Map of temporary IDs that have been resolved to actual issue/PR/discussion numbers
+ * @param temporaryIdMap - Live Map of temporary IDs for in-flight substitutions (may be used by action handlers)
  * @returns Promise resolving to result with success status and details
  */
-type MessageHandlerFunction = (message: any, resolvedTemporaryIds: ResolvedTemporaryIds) => Promise<HandlerResult>;
+type MessageHandlerFunction = (message: any, resolvedTemporaryIds: ResolvedTemporaryIds, temporaryIdMap?: Map<string, any>) => Promise<HandlerResult>;
 
 /**
  * Main factory function signature for safe output handlers

From ead4e55897c98335b3e311889a187d341cdd8a82 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 14:51:07 +0000
Subject: [PATCH 08/17] Co-authored-by: pelikhan
 <4175913+pelikhan@users.noreply.github.com>

---
 .github/workflows/smoke-codex.lock.yml | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index b89c3a49766..d8d72613fb0 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -1561,6 +1561,15 @@ jobs:
             setupGlobals(core, github, context, exec, io);
             const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs');
             await main();
+      - name: Add the 'smoked' label to the current pull request
+        id: action_add_smoked_label
+        if: steps.process_safe_outputs.outputs.action_add_smoked_label_payload != ''
+        uses: actions-ecosystem/action-add-labels@18f1af5e3544586314bbe15c0273249c770b2daf # v1
+        env:
+          GITHUB_TOKEN: ${{ github.token }}
+        with:
+          labels: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).labels }}
+          number: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).number }}
       - name: Upload safe output items
         if: always()
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0

From bdc20c17df25c4e83332b770c7396856c2ae0f9b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 14:56:29 +0000
Subject: [PATCH 09/17] chore: recompile workflows

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .github/workflows/smoke-codex.lock.yml | 16 ++++------------
 1 file changed, 4 insertions(+), 12 deletions(-)

diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index d8d72613fb0..2c73bf9bcab 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -450,20 +450,13 @@ jobs:
               {
                 "description": "Add the 'smoked' label to the current pull request (can only be called once)",
                 "inputSchema": {
-                  "additionalProperties": false,
+                  "additionalProperties": true,
                   "properties": {
-                    "labels": {
-                      "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.",
-                      "type": "string"
-                    },
-                    "number": {
-                      "description": "The number of the issue or pull request.",
+                    "payload": {
+                      "description": "JSON-encoded payload to pass to the action",
                       "type": "string"
                     }
                   },
-                  "required": [
-                    "labels"
-                  ],
                   "type": "object"
                 },
                 "name": "add_smoked_label"
@@ -1568,8 +1561,7 @@ jobs:
         env:
           GITHUB_TOKEN: ${{ github.token }}
         with:
-          labels: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).labels }}
-          number: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).number }}
+          payload: ${{ steps.process_safe_outputs.outputs.action_add_smoked_label_payload }}
       - name: Upload safe output items
         if: always()
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0

From 7a554f931cee6b7e0035c88214d3d3d336f166b6 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 19 Mar 2026 14:58:37 +0000
Subject: [PATCH 10/17] Add changeset [skip-ci]

---
 .changeset/patch-add-safe-output-actions.md | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 .changeset/patch-add-safe-output-actions.md

diff --git a/.changeset/patch-add-safe-output-actions.md b/.changeset/patch-add-safe-output-actions.md
new file mode 100644
index 00000000000..664ce396aab
--- /dev/null
+++ b/.changeset/patch-add-safe-output-actions.md
@@ -0,0 +1,5 @@
+---
+"gh-aw": patch
+---
+
+Add support for `safe-outputs.actions` to mount custom GitHub Actions as once-callable safe output tools, including compile-time action metadata resolution and generated guarded workflow steps.

From 67d4dd5a75f61e1362c34c3a9b83ca6da53a97f3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 15:10:24 +0000
Subject: [PATCH 11/17] fix: add continue-on-error to Upload safe output items
 step to prevent 409 conflict

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .github/workflows/agent-performance-analyzer.lock.yml         | 1 +
 .github/workflows/agent-persona-explorer.lock.yml             | 1 +
 .github/workflows/ai-moderator.lock.yml                       | 1 +
 .github/workflows/archie.lock.yml                             | 1 +
 .github/workflows/artifacts-summary.lock.yml                  | 1 +
 .github/workflows/audit-workflows.lock.yml                    | 1 +
 .github/workflows/auto-triage-issues.lock.yml                 | 1 +
 .github/workflows/blog-auditor.lock.yml                       | 1 +
 .github/workflows/bot-detection.lock.yml                      | 1 +
 .github/workflows/brave.lock.yml                              | 1 +
 .github/workflows/breaking-change-checker.lock.yml            | 1 +
 .github/workflows/changeset.lock.yml                          | 1 +
 .github/workflows/ci-coach.lock.yml                           | 1 +
 .github/workflows/ci-doctor.lock.yml                          | 1 +
 .github/workflows/claude-code-user-docs-review.lock.yml       | 1 +
 .github/workflows/cli-consistency-checker.lock.yml            | 1 +
 .github/workflows/cli-version-checker.lock.yml                | 1 +
 .github/workflows/cloclo.lock.yml                             | 1 +
 .github/workflows/code-scanning-fixer.lock.yml                | 1 +
 .github/workflows/code-simplifier.lock.yml                    | 1 +
 .github/workflows/commit-changes-analyzer.lock.yml            | 1 +
 .github/workflows/constraint-solving-potd.lock.yml            | 1 +
 .github/workflows/contribution-check.lock.yml                 | 1 +
 .github/workflows/copilot-agent-analysis.lock.yml             | 1 +
 .github/workflows/copilot-cli-deep-research.lock.yml          | 1 +
 .github/workflows/copilot-pr-merged-report.lock.yml           | 1 +
 .github/workflows/copilot-pr-nlp-analysis.lock.yml            | 1 +
 .github/workflows/copilot-pr-prompt-analysis.lock.yml         | 1 +
 .github/workflows/copilot-session-insights.lock.yml           | 1 +
 .github/workflows/craft.lock.yml                              | 1 +
 .github/workflows/daily-architecture-diagram.lock.yml         | 1 +
 .github/workflows/daily-assign-issue-to-user.lock.yml         | 1 +
 .github/workflows/daily-cli-performance.lock.yml              | 1 +
 .github/workflows/daily-cli-tools-tester.lock.yml             | 1 +
 .github/workflows/daily-code-metrics.lock.yml                 | 1 +
 .github/workflows/daily-compiler-quality.lock.yml             | 1 +
 .github/workflows/daily-copilot-token-report.lock.yml         | 1 +
 .github/workflows/daily-doc-healer.lock.yml                   | 1 +
 .github/workflows/daily-doc-updater.lock.yml                  | 1 +
 .github/workflows/daily-fact.lock.yml                         | 1 +
 .github/workflows/daily-file-diet.lock.yml                    | 1 +
 .github/workflows/daily-firewall-report.lock.yml              | 1 +
 .github/workflows/daily-function-namer.lock.yml               | 1 +
 .github/workflows/daily-issues-report.lock.yml                | 1 +
 .github/workflows/daily-malicious-code-scan.lock.yml          | 1 +
 .github/workflows/daily-mcp-concurrency-analysis.lock.yml     | 1 +
 .github/workflows/daily-multi-device-docs-tester.lock.yml     | 1 +
 .github/workflows/daily-news.lock.yml                         | 1 +
 .github/workflows/daily-observability-report.lock.yml         | 1 +
 .github/workflows/daily-performance-summary.lock.yml          | 1 +
 .github/workflows/daily-regulatory.lock.yml                   | 1 +
 .github/workflows/daily-rendering-scripts-verifier.lock.yml   | 1 +
 .github/workflows/daily-repo-chronicle.lock.yml               | 1 +
 .github/workflows/daily-safe-output-integrator.lock.yml       | 1 +
 .github/workflows/daily-safe-output-optimizer.lock.yml        | 1 +
 .github/workflows/daily-safe-outputs-conformance.lock.yml     | 1 +
 .github/workflows/daily-secrets-analysis.lock.yml             | 1 +
 .github/workflows/daily-security-red-team.lock.yml            | 1 +
 .github/workflows/daily-semgrep-scan.lock.yml                 | 1 +
 .github/workflows/daily-syntax-error-quality.lock.yml         | 1 +
 .github/workflows/daily-team-evolution-insights.lock.yml      | 1 +
 .github/workflows/daily-team-status.lock.yml                  | 1 +
 .github/workflows/daily-testify-uber-super-expert.lock.yml    | 1 +
 .github/workflows/daily-workflow-updater.lock.yml             | 1 +
 .github/workflows/dead-code-remover.lock.yml                  | 1 +
 .github/workflows/deep-report.lock.yml                        | 1 +
 .github/workflows/delight.lock.yml                            | 1 +
 .github/workflows/dependabot-burner.lock.yml                  | 1 +
 .github/workflows/dependabot-go-checker.lock.yml              | 1 +
 .github/workflows/dev-hawk.lock.yml                           | 1 +
 .github/workflows/dev.lock.yml                                | 1 +
 .github/workflows/developer-docs-consolidator.lock.yml        | 1 +
 .github/workflows/dictation-prompt.lock.yml                   | 1 +
 .github/workflows/discussion-task-miner.lock.yml              | 1 +
 .github/workflows/docs-noob-tester.lock.yml                   | 1 +
 .github/workflows/draft-pr-cleanup.lock.yml                   | 1 +
 .github/workflows/duplicate-code-detector.lock.yml            | 1 +
 .github/workflows/example-workflow-analyzer.lock.yml          | 1 +
 .github/workflows/firewall-escape.lock.yml                    | 1 +
 .github/workflows/functional-pragmatist.lock.yml              | 1 +
 .github/workflows/github-mcp-structural-analysis.lock.yml     | 1 +
 .github/workflows/github-mcp-tools-report.lock.yml            | 1 +
 .github/workflows/github-remote-mcp-auth-test.lock.yml        | 1 +
 .github/workflows/glossary-maintainer.lock.yml                | 1 +
 .github/workflows/go-fan.lock.yml                             | 1 +
 .github/workflows/go-logger.lock.yml                          | 1 +
 .github/workflows/go-pattern-detector.lock.yml                | 1 +
 .github/workflows/gpclean.lock.yml                            | 1 +
 .github/workflows/grumpy-reviewer.lock.yml                    | 1 +
 .github/workflows/hourly-ci-cleaner.lock.yml                  | 1 +
 .github/workflows/instructions-janitor.lock.yml               | 1 +
 .github/workflows/issue-arborist.lock.yml                     | 1 +
 .github/workflows/issue-monster.lock.yml                      | 1 +
 .github/workflows/issue-triage-agent.lock.yml                 | 1 +
 .github/workflows/jsweep.lock.yml                             | 1 +
 .github/workflows/layout-spec-maintainer.lock.yml             | 1 +
 .github/workflows/lockfile-stats.lock.yml                     | 1 +
 .github/workflows/mcp-inspector.lock.yml                      | 1 +
 .github/workflows/mergefest.lock.yml                          | 1 +
 .github/workflows/notion-issue-summary.lock.yml               | 1 +
 .github/workflows/org-health-report.lock.yml                  | 1 +
 .github/workflows/pdf-summary.lock.yml                        | 1 +
 .github/workflows/plan.lock.yml                               | 1 +
 .github/workflows/portfolio-analyst.lock.yml                  | 1 +
 .github/workflows/pr-nitpick-reviewer.lock.yml                | 1 +
 .github/workflows/pr-triage-agent.lock.yml                    | 1 +
 .github/workflows/prompt-clustering-analysis.lock.yml         | 1 +
 .github/workflows/python-data-charts.lock.yml                 | 1 +
 .github/workflows/q.lock.yml                                  | 1 +
 .github/workflows/refiner.lock.yml                            | 1 +
 .github/workflows/release.lock.yml                            | 1 +
 .github/workflows/repo-audit-analyzer.lock.yml                | 1 +
 .github/workflows/repo-tree-map.lock.yml                      | 1 +
 .github/workflows/repository-quality-improver.lock.yml        | 1 +
 .github/workflows/research.lock.yml                           | 1 +
 .github/workflows/safe-output-health.lock.yml                 | 1 +
 .github/workflows/schema-consistency-checker.lock.yml         | 1 +
 .github/workflows/scout.lock.yml                              | 1 +
 .github/workflows/security-alert-burndown.campaign.g.lock.yml | 1 +
 .github/workflows/security-compliance.lock.yml                | 1 +
 .github/workflows/security-review.lock.yml                    | 1 +
 .github/workflows/semantic-function-refactor.lock.yml         | 1 +
 .github/workflows/sergo.lock.yml                              | 1 +
 .github/workflows/slide-deck-maintainer.lock.yml              | 1 +
 .github/workflows/smoke-agent-all-merged.lock.yml             | 1 +
 .github/workflows/smoke-agent-all-none.lock.yml               | 1 +
 .github/workflows/smoke-agent-public-approved.lock.yml        | 1 +
 .github/workflows/smoke-agent-public-none.lock.yml            | 1 +
 .github/workflows/smoke-agent-scoped-approved.lock.yml        | 1 +
 .github/workflows/smoke-call-workflow.lock.yml                | 1 +
 .github/workflows/smoke-claude.lock.yml                       | 1 +
 .github/workflows/smoke-codex.lock.yml                        | 1 +
 .github/workflows/smoke-copilot-arm.lock.yml                  | 1 +
 .github/workflows/smoke-copilot.lock.yml                      | 1 +
 .github/workflows/smoke-create-cross-repo-pr.lock.yml         | 1 +
 .github/workflows/smoke-gemini.lock.yml                       | 1 +
 .github/workflows/smoke-multi-pr.lock.yml                     | 1 +
 .github/workflows/smoke-project.lock.yml                      | 1 +
 .github/workflows/smoke-temporary-id.lock.yml                 | 1 +
 .github/workflows/smoke-test-tools.lock.yml                   | 1 +
 .github/workflows/smoke-update-cross-repo-pr.lock.yml         | 1 +
 .github/workflows/smoke-workflow-call-with-inputs.lock.yml    | 1 +
 .github/workflows/smoke-workflow-call.lock.yml                | 1 +
 .github/workflows/stale-repo-identifier.lock.yml              | 1 +
 .github/workflows/static-analysis-report.lock.yml             | 1 +
 .github/workflows/step-name-alignment.lock.yml                | 1 +
 .github/workflows/sub-issue-closer.lock.yml                   | 1 +
 .github/workflows/super-linter.lock.yml                       | 1 +
 .github/workflows/technical-doc-writer.lock.yml               | 1 +
 .github/workflows/terminal-stylist.lock.yml                   | 1 +
 .github/workflows/test-create-pr-error-handling.lock.yml      | 1 +
 .github/workflows/test-dispatcher.lock.yml                    | 1 +
 .github/workflows/test-project-url-default.lock.yml           | 1 +
 .github/workflows/tidy.lock.yml                               | 1 +
 .github/workflows/typist.lock.yml                             | 1 +
 .github/workflows/ubuntu-image-analyzer.lock.yml              | 1 +
 .github/workflows/unbloat-docs.lock.yml                       | 1 +
 .github/workflows/update-astro.lock.yml                       | 1 +
 .github/workflows/video-analyzer.lock.yml                     | 1 +
 .github/workflows/weekly-blog-post-writer.lock.yml            | 1 +
 .github/workflows/weekly-editors-health-check.lock.yml        | 1 +
 .github/workflows/weekly-issue-summary.lock.yml               | 1 +
 .github/workflows/weekly-safe-outputs-spec-review.lock.yml    | 1 +
 .github/workflows/workflow-generator.lock.yml                 | 1 +
 .github/workflows/workflow-health-manager.lock.yml            | 1 +
 .github/workflows/workflow-normalizer.lock.yml                | 1 +
 .github/workflows/workflow-skill-extractor.lock.yml           | 1 +
 pkg/workflow/compiler_safe_outputs_job.go                     | 4 ++++
 168 files changed, 171 insertions(+)

diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml
index 7e50bc6e71a..f1b27546ecd 100644
--- a/.github/workflows/agent-performance-analyzer.lock.yml
+++ b/.github/workflows/agent-performance-analyzer.lock.yml
@@ -1313,6 +1313,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/agent-persona-explorer.lock.yml b/.github/workflows/agent-persona-explorer.lock.yml
index eaa7f009d69..125a09d7d68 100644
--- a/.github/workflows/agent-persona-explorer.lock.yml
+++ b/.github/workflows/agent-persona-explorer.lock.yml
@@ -1173,6 +1173,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/ai-moderator.lock.yml b/.github/workflows/ai-moderator.lock.yml
index 71088a0ea1f..cd0015c3d3c 100644
--- a/.github/workflows/ai-moderator.lock.yml
+++ b/.github/workflows/ai-moderator.lock.yml
@@ -1036,6 +1036,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml
index 589db97cd65..84173a58ee3 100644
--- a/.github/workflows/archie.lock.yml
+++ b/.github/workflows/archie.lock.yml
@@ -1192,6 +1192,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml
index 52bd94d7bd6..8545f157517 100644
--- a/.github/workflows/artifacts-summary.lock.yml
+++ b/.github/workflows/artifacts-summary.lock.yml
@@ -1088,6 +1088,7 @@ jobs:
           echo "Token invalidation step complete."
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml
index 41a55bfbbfd..767b500259a 100644
--- a/.github/workflows/audit-workflows.lock.yml
+++ b/.github/workflows/audit-workflows.lock.yml
@@ -1370,6 +1370,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/auto-triage-issues.lock.yml b/.github/workflows/auto-triage-issues.lock.yml
index 6b809476a98..4d6f00a5614 100644
--- a/.github/workflows/auto-triage-issues.lock.yml
+++ b/.github/workflows/auto-triage-issues.lock.yml
@@ -1134,6 +1134,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml
index 1782a2e67e5..28ccddd5916 100644
--- a/.github/workflows/blog-auditor.lock.yml
+++ b/.github/workflows/blog-auditor.lock.yml
@@ -1166,6 +1166,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/bot-detection.lock.yml b/.github/workflows/bot-detection.lock.yml
index 9e939b2c6b9..ff8b6b85c7f 100644
--- a/.github/workflows/bot-detection.lock.yml
+++ b/.github/workflows/bot-detection.lock.yml
@@ -1791,6 +1791,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml
index 1b0c6c86d84..b0b291cfee3 100644
--- a/.github/workflows/brave.lock.yml
+++ b/.github/workflows/brave.lock.yml
@@ -1177,6 +1177,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/breaking-change-checker.lock.yml b/.github/workflows/breaking-change-checker.lock.yml
index dbf218e5b45..360707bc98a 100644
--- a/.github/workflows/breaking-change-checker.lock.yml
+++ b/.github/workflows/breaking-change-checker.lock.yml
@@ -1142,6 +1142,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml
index e2f36649cd0..69cbf15676b 100644
--- a/.github/workflows/changeset.lock.yml
+++ b/.github/workflows/changeset.lock.yml
@@ -1156,6 +1156,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/ci-coach.lock.yml b/.github/workflows/ci-coach.lock.yml
index 4957659c643..f3005314814 100644
--- a/.github/workflows/ci-coach.lock.yml
+++ b/.github/workflows/ci-coach.lock.yml
@@ -1178,6 +1178,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml
index 7dc32a49f6b..393f638379a 100644
--- a/.github/workflows/ci-doctor.lock.yml
+++ b/.github/workflows/ci-doctor.lock.yml
@@ -1245,6 +1245,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/claude-code-user-docs-review.lock.yml b/.github/workflows/claude-code-user-docs-review.lock.yml
index 98c33196d63..b1f83c31a1d 100644
--- a/.github/workflows/claude-code-user-docs-review.lock.yml
+++ b/.github/workflows/claude-code-user-docs-review.lock.yml
@@ -1127,6 +1127,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml
index 4a872c0ab0e..b0797d0141c 100644
--- a/.github/workflows/cli-consistency-checker.lock.yml
+++ b/.github/workflows/cli-consistency-checker.lock.yml
@@ -1031,6 +1031,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml
index 3488192b8d2..3d4b0cadb89 100644
--- a/.github/workflows/cli-version-checker.lock.yml
+++ b/.github/workflows/cli-version-checker.lock.yml
@@ -1132,6 +1132,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml
index 7ebd09206c2..83657b9e472 100644
--- a/.github/workflows/cloclo.lock.yml
+++ b/.github/workflows/cloclo.lock.yml
@@ -1583,6 +1583,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml
index 90931d5f46d..a3aecee2068 100644
--- a/.github/workflows/code-scanning-fixer.lock.yml
+++ b/.github/workflows/code-scanning-fixer.lock.yml
@@ -1293,6 +1293,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/code-simplifier.lock.yml b/.github/workflows/code-simplifier.lock.yml
index f47de0b83a1..5fa2492f000 100644
--- a/.github/workflows/code-simplifier.lock.yml
+++ b/.github/workflows/code-simplifier.lock.yml
@@ -1170,6 +1170,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml
index 485bcfce3b6..86f98cf1e2b 100644
--- a/.github/workflows/commit-changes-analyzer.lock.yml
+++ b/.github/workflows/commit-changes-analyzer.lock.yml
@@ -1096,6 +1096,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/constraint-solving-potd.lock.yml b/.github/workflows/constraint-solving-potd.lock.yml
index c190b12157b..d9f1687c46c 100644
--- a/.github/workflows/constraint-solving-potd.lock.yml
+++ b/.github/workflows/constraint-solving-potd.lock.yml
@@ -1036,6 +1036,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml
index a415068d8d1..b32fc7ce478 100644
--- a/.github/workflows/contribution-check.lock.yml
+++ b/.github/workflows/contribution-check.lock.yml
@@ -1074,6 +1074,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml
index a120736984f..edb4305214d 100644
--- a/.github/workflows/copilot-agent-analysis.lock.yml
+++ b/.github/workflows/copilot-agent-analysis.lock.yml
@@ -1249,6 +1249,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/copilot-cli-deep-research.lock.yml b/.github/workflows/copilot-cli-deep-research.lock.yml
index ead9c43c245..e8ba3e02176 100644
--- a/.github/workflows/copilot-cli-deep-research.lock.yml
+++ b/.github/workflows/copilot-cli-deep-research.lock.yml
@@ -1168,6 +1168,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml
index 07ef1bd12d5..f707149d704 100644
--- a/.github/workflows/copilot-pr-merged-report.lock.yml
+++ b/.github/workflows/copilot-pr-merged-report.lock.yml
@@ -1212,6 +1212,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
index 0b22bf5524f..f52f4a3afd3 100644
--- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
@@ -1252,6 +1252,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
index 0fb5275d2ca..79fc0786667 100644
--- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
@@ -1188,6 +1188,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml
index 7b126a037c6..5e6c2785ca3 100644
--- a/.github/workflows/copilot-session-insights.lock.yml
+++ b/.github/workflows/copilot-session-insights.lock.yml
@@ -1312,6 +1312,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml
index 1534e88c420..702378c8588 100644
--- a/.github/workflows/craft.lock.yml
+++ b/.github/workflows/craft.lock.yml
@@ -1211,6 +1211,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-architecture-diagram.lock.yml b/.github/workflows/daily-architecture-diagram.lock.yml
index 0632a159c06..f929c072ed6 100644
--- a/.github/workflows/daily-architecture-diagram.lock.yml
+++ b/.github/workflows/daily-architecture-diagram.lock.yml
@@ -1152,6 +1152,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-assign-issue-to-user.lock.yml b/.github/workflows/daily-assign-issue-to-user.lock.yml
index 6ef8de70771..6a44a615558 100644
--- a/.github/workflows/daily-assign-issue-to-user.lock.yml
+++ b/.github/workflows/daily-assign-issue-to-user.lock.yml
@@ -1040,6 +1040,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml
index 4b7b81197fd..9e34c5593a0 100644
--- a/.github/workflows/daily-cli-performance.lock.yml
+++ b/.github/workflows/daily-cli-performance.lock.yml
@@ -1337,6 +1337,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-cli-tools-tester.lock.yml b/.github/workflows/daily-cli-tools-tester.lock.yml
index 942a4651bd3..f857169dc5d 100644
--- a/.github/workflows/daily-cli-tools-tester.lock.yml
+++ b/.github/workflows/daily-cli-tools-tester.lock.yml
@@ -1119,6 +1119,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml
index 108369b05c3..538557459ae 100644
--- a/.github/workflows/daily-code-metrics.lock.yml
+++ b/.github/workflows/daily-code-metrics.lock.yml
@@ -1291,6 +1291,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-compiler-quality.lock.yml b/.github/workflows/daily-compiler-quality.lock.yml
index e124fcf4329..7db8cbd068d 100644
--- a/.github/workflows/daily-compiler-quality.lock.yml
+++ b/.github/workflows/daily-compiler-quality.lock.yml
@@ -1102,6 +1102,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-copilot-token-report.lock.yml b/.github/workflows/daily-copilot-token-report.lock.yml
index df1b803bc91..0438f312841 100644
--- a/.github/workflows/daily-copilot-token-report.lock.yml
+++ b/.github/workflows/daily-copilot-token-report.lock.yml
@@ -1261,6 +1261,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-doc-healer.lock.yml b/.github/workflows/daily-doc-healer.lock.yml
index 7af814d0d47..04e221e6f27 100644
--- a/.github/workflows/daily-doc-healer.lock.yml
+++ b/.github/workflows/daily-doc-healer.lock.yml
@@ -1406,6 +1406,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml
index e1fb9065a84..653f832dcd8 100644
--- a/.github/workflows/daily-doc-updater.lock.yml
+++ b/.github/workflows/daily-doc-updater.lock.yml
@@ -1354,6 +1354,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-fact.lock.yml b/.github/workflows/daily-fact.lock.yml
index 9f36d87e940..5436aa89387 100644
--- a/.github/workflows/daily-fact.lock.yml
+++ b/.github/workflows/daily-fact.lock.yml
@@ -1053,6 +1053,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml
index c7743f4135d..2d73ec5d975 100644
--- a/.github/workflows/daily-file-diet.lock.yml
+++ b/.github/workflows/daily-file-diet.lock.yml
@@ -1203,6 +1203,7 @@ jobs:
           echo "Token invalidation step complete."
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml
index b7693129bce..0b8f27e7923 100644
--- a/.github/workflows/daily-firewall-report.lock.yml
+++ b/.github/workflows/daily-firewall-report.lock.yml
@@ -1198,6 +1198,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-function-namer.lock.yml b/.github/workflows/daily-function-namer.lock.yml
index de0848ed99a..6a81ad29078 100644
--- a/.github/workflows/daily-function-namer.lock.yml
+++ b/.github/workflows/daily-function-namer.lock.yml
@@ -1169,6 +1169,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml
index b911c8bf7e7..4541e3bd234 100644
--- a/.github/workflows/daily-issues-report.lock.yml
+++ b/.github/workflows/daily-issues-report.lock.yml
@@ -1205,6 +1205,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-malicious-code-scan.lock.yml b/.github/workflows/daily-malicious-code-scan.lock.yml
index 538da9a5716..a4d32ac7f8f 100644
--- a/.github/workflows/daily-malicious-code-scan.lock.yml
+++ b/.github/workflows/daily-malicious-code-scan.lock.yml
@@ -919,6 +919,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
index 02ab045a72d..535f6733c29 100644
--- a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
+++ b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
@@ -1188,6 +1188,7 @@ jobs:
           echo "Token invalidation step complete."
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml
index a3dc711a67d..43abbc1a072 100644
--- a/.github/workflows/daily-multi-device-docs-tester.lock.yml
+++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml
@@ -1214,6 +1214,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml
index c7472b56b42..4afa6abe7e3 100644
--- a/.github/workflows/daily-news.lock.yml
+++ b/.github/workflows/daily-news.lock.yml
@@ -1329,6 +1329,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-observability-report.lock.yml b/.github/workflows/daily-observability-report.lock.yml
index bf3431db939..8d2c0832b8e 100644
--- a/.github/workflows/daily-observability-report.lock.yml
+++ b/.github/workflows/daily-observability-report.lock.yml
@@ -1181,6 +1181,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml
index eadc6195ebc..2dfdd528ee5 100644
--- a/.github/workflows/daily-performance-summary.lock.yml
+++ b/.github/workflows/daily-performance-summary.lock.yml
@@ -1640,6 +1640,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-regulatory.lock.yml b/.github/workflows/daily-regulatory.lock.yml
index 050c428220a..3a854644372 100644
--- a/.github/workflows/daily-regulatory.lock.yml
+++ b/.github/workflows/daily-regulatory.lock.yml
@@ -1561,6 +1561,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-rendering-scripts-verifier.lock.yml b/.github/workflows/daily-rendering-scripts-verifier.lock.yml
index b5f5afa7f8d..a4f3955e8ea 100644
--- a/.github/workflows/daily-rendering-scripts-verifier.lock.yml
+++ b/.github/workflows/daily-rendering-scripts-verifier.lock.yml
@@ -1356,6 +1356,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml
index 8d814b87b3a..c25be968703 100644
--- a/.github/workflows/daily-repo-chronicle.lock.yml
+++ b/.github/workflows/daily-repo-chronicle.lock.yml
@@ -1120,6 +1120,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-safe-output-integrator.lock.yml b/.github/workflows/daily-safe-output-integrator.lock.yml
index 2e06453ec7d..97685848898 100644
--- a/.github/workflows/daily-safe-output-integrator.lock.yml
+++ b/.github/workflows/daily-safe-output-integrator.lock.yml
@@ -1134,6 +1134,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-safe-output-optimizer.lock.yml b/.github/workflows/daily-safe-output-optimizer.lock.yml
index 893b52e42bb..09a1423a699 100644
--- a/.github/workflows/daily-safe-output-optimizer.lock.yml
+++ b/.github/workflows/daily-safe-output-optimizer.lock.yml
@@ -1289,6 +1289,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-safe-outputs-conformance.lock.yml b/.github/workflows/daily-safe-outputs-conformance.lock.yml
index eae081cacdf..9ee64804a4b 100644
--- a/.github/workflows/daily-safe-outputs-conformance.lock.yml
+++ b/.github/workflows/daily-safe-outputs-conformance.lock.yml
@@ -1100,6 +1100,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-secrets-analysis.lock.yml b/.github/workflows/daily-secrets-analysis.lock.yml
index 4bbaf69c8d6..6406744be43 100644
--- a/.github/workflows/daily-secrets-analysis.lock.yml
+++ b/.github/workflows/daily-secrets-analysis.lock.yml
@@ -1067,6 +1067,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-security-red-team.lock.yml b/.github/workflows/daily-security-red-team.lock.yml
index 8cb68e138be..5f08891c9dd 100644
--- a/.github/workflows/daily-security-red-team.lock.yml
+++ b/.github/workflows/daily-security-red-team.lock.yml
@@ -1104,6 +1104,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-semgrep-scan.lock.yml b/.github/workflows/daily-semgrep-scan.lock.yml
index cacfdef810a..4b399d134df 100644
--- a/.github/workflows/daily-semgrep-scan.lock.yml
+++ b/.github/workflows/daily-semgrep-scan.lock.yml
@@ -1066,6 +1066,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-syntax-error-quality.lock.yml b/.github/workflows/daily-syntax-error-quality.lock.yml
index d88bf17404a..5642a5f743d 100644
--- a/.github/workflows/daily-syntax-error-quality.lock.yml
+++ b/.github/workflows/daily-syntax-error-quality.lock.yml
@@ -1077,6 +1077,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-team-evolution-insights.lock.yml b/.github/workflows/daily-team-evolution-insights.lock.yml
index 001e1eecfa4..df933be5809 100644
--- a/.github/workflows/daily-team-evolution-insights.lock.yml
+++ b/.github/workflows/daily-team-evolution-insights.lock.yml
@@ -1098,6 +1098,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-team-status.lock.yml b/.github/workflows/daily-team-status.lock.yml
index c102861d3f2..1a47286aa9f 100644
--- a/.github/workflows/daily-team-status.lock.yml
+++ b/.github/workflows/daily-team-status.lock.yml
@@ -1099,6 +1099,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml
index 9c9273b4d78..df7f45fec1a 100644
--- a/.github/workflows/daily-testify-uber-super-expert.lock.yml
+++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml
@@ -1313,6 +1313,7 @@ jobs:
           echo "Token invalidation step complete."
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/daily-workflow-updater.lock.yml b/.github/workflows/daily-workflow-updater.lock.yml
index 670d33c8528..9328c2ec008 100644
--- a/.github/workflows/daily-workflow-updater.lock.yml
+++ b/.github/workflows/daily-workflow-updater.lock.yml
@@ -1091,6 +1091,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/dead-code-remover.lock.yml b/.github/workflows/dead-code-remover.lock.yml
index e3b83cbef1a..a95969f1d8c 100644
--- a/.github/workflows/dead-code-remover.lock.yml
+++ b/.github/workflows/dead-code-remover.lock.yml
@@ -1193,6 +1193,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml
index 57960c0e01f..347b7df84fd 100644
--- a/.github/workflows/deep-report.lock.yml
+++ b/.github/workflows/deep-report.lock.yml
@@ -1340,6 +1340,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/delight.lock.yml b/.github/workflows/delight.lock.yml
index 00fb96165d2..c9c837f7061 100644
--- a/.github/workflows/delight.lock.yml
+++ b/.github/workflows/delight.lock.yml
@@ -1217,6 +1217,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/dependabot-burner.lock.yml b/.github/workflows/dependabot-burner.lock.yml
index 4a8faee6d9f..e2fd6da772f 100644
--- a/.github/workflows/dependabot-burner.lock.yml
+++ b/.github/workflows/dependabot-burner.lock.yml
@@ -1076,6 +1076,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml
index b3525a653b2..b0e15842eef 100644
--- a/.github/workflows/dependabot-go-checker.lock.yml
+++ b/.github/workflows/dependabot-go-checker.lock.yml
@@ -1062,6 +1062,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml
index 6c752ab2082..df7f6d070f1 100644
--- a/.github/workflows/dev-hawk.lock.yml
+++ b/.github/workflows/dev-hawk.lock.yml
@@ -1181,6 +1181,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml
index 957afa93a23..20ce1679b49 100644
--- a/.github/workflows/dev.lock.yml
+++ b/.github/workflows/dev.lock.yml
@@ -1028,6 +1028,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml
index aa2ba94b568..e6b7d5ae359 100644
--- a/.github/workflows/developer-docs-consolidator.lock.yml
+++ b/.github/workflows/developer-docs-consolidator.lock.yml
@@ -1520,6 +1520,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/dictation-prompt.lock.yml b/.github/workflows/dictation-prompt.lock.yml
index a2899383cb9..c5b79d1b138 100644
--- a/.github/workflows/dictation-prompt.lock.yml
+++ b/.github/workflows/dictation-prompt.lock.yml
@@ -1238,6 +1238,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/discussion-task-miner.lock.yml b/.github/workflows/discussion-task-miner.lock.yml
index 2419a6f0f3d..46622dfc1cd 100644
--- a/.github/workflows/discussion-task-miner.lock.yml
+++ b/.github/workflows/discussion-task-miner.lock.yml
@@ -1211,6 +1211,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml
index cf7c9bd113c..b96c8a2d53a 100644
--- a/.github/workflows/docs-noob-tester.lock.yml
+++ b/.github/workflows/docs-noob-tester.lock.yml
@@ -1082,6 +1082,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/draft-pr-cleanup.lock.yml b/.github/workflows/draft-pr-cleanup.lock.yml
index 8f42adfdb8a..ac789bd6c10 100644
--- a/.github/workflows/draft-pr-cleanup.lock.yml
+++ b/.github/workflows/draft-pr-cleanup.lock.yml
@@ -1076,6 +1076,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml
index 63b29e2cf5c..a3dbdeb5a47 100644
--- a/.github/workflows/duplicate-code-detector.lock.yml
+++ b/.github/workflows/duplicate-code-detector.lock.yml
@@ -1097,6 +1097,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml
index 8876483f255..2f6986f901f 100644
--- a/.github/workflows/example-workflow-analyzer.lock.yml
+++ b/.github/workflows/example-workflow-analyzer.lock.yml
@@ -1164,6 +1164,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/firewall-escape.lock.yml b/.github/workflows/firewall-escape.lock.yml
index 20bd45d0575..5807c3cdd27 100644
--- a/.github/workflows/firewall-escape.lock.yml
+++ b/.github/workflows/firewall-escape.lock.yml
@@ -1259,6 +1259,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/functional-pragmatist.lock.yml b/.github/workflows/functional-pragmatist.lock.yml
index c4fd605bc1d..b3957cdf979 100644
--- a/.github/workflows/functional-pragmatist.lock.yml
+++ b/.github/workflows/functional-pragmatist.lock.yml
@@ -1104,6 +1104,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml
index 26304081646..b4c3ad69ecd 100644
--- a/.github/workflows/github-mcp-structural-analysis.lock.yml
+++ b/.github/workflows/github-mcp-structural-analysis.lock.yml
@@ -1176,6 +1176,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml
index 68d4a7ae685..fa9c56f3ded 100644
--- a/.github/workflows/github-mcp-tools-report.lock.yml
+++ b/.github/workflows/github-mcp-tools-report.lock.yml
@@ -1213,6 +1213,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/github-remote-mcp-auth-test.lock.yml b/.github/workflows/github-remote-mcp-auth-test.lock.yml
index 498e7d61aff..58af158312a 100644
--- a/.github/workflows/github-remote-mcp-auth-test.lock.yml
+++ b/.github/workflows/github-remote-mcp-auth-test.lock.yml
@@ -1045,6 +1045,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml
index e999c40c391..0dec5955a4a 100644
--- a/.github/workflows/glossary-maintainer.lock.yml
+++ b/.github/workflows/glossary-maintainer.lock.yml
@@ -1441,6 +1441,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml
index 0f9bf2fd62e..d08db5c522e 100644
--- a/.github/workflows/go-fan.lock.yml
+++ b/.github/workflows/go-fan.lock.yml
@@ -1170,6 +1170,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml
index 17972f46119..dd5c2b4fb22 100644
--- a/.github/workflows/go-logger.lock.yml
+++ b/.github/workflows/go-logger.lock.yml
@@ -1373,6 +1373,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml
index 938ff45d683..7fb666f4605 100644
--- a/.github/workflows/go-pattern-detector.lock.yml
+++ b/.github/workflows/go-pattern-detector.lock.yml
@@ -1163,6 +1163,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/gpclean.lock.yml b/.github/workflows/gpclean.lock.yml
index dc8ab39c117..4db81b8c4b0 100644
--- a/.github/workflows/gpclean.lock.yml
+++ b/.github/workflows/gpclean.lock.yml
@@ -1072,6 +1072,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml
index 29b6b91aad4..a6924be62c9 100644
--- a/.github/workflows/grumpy-reviewer.lock.yml
+++ b/.github/workflows/grumpy-reviewer.lock.yml
@@ -1217,6 +1217,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/hourly-ci-cleaner.lock.yml b/.github/workflows/hourly-ci-cleaner.lock.yml
index 660b454beb5..ec387f588c1 100644
--- a/.github/workflows/hourly-ci-cleaner.lock.yml
+++ b/.github/workflows/hourly-ci-cleaner.lock.yml
@@ -1211,6 +1211,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml
index 78df62b7e79..248244f987a 100644
--- a/.github/workflows/instructions-janitor.lock.yml
+++ b/.github/workflows/instructions-janitor.lock.yml
@@ -1194,6 +1194,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml
index 378da866003..87a2d2ac307 100644
--- a/.github/workflows/issue-arborist.lock.yml
+++ b/.github/workflows/issue-arborist.lock.yml
@@ -1095,6 +1095,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml
index efd91245e30..0eed28dc01f 100644
--- a/.github/workflows/issue-monster.lock.yml
+++ b/.github/workflows/issue-monster.lock.yml
@@ -1838,6 +1838,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/issue-triage-agent.lock.yml b/.github/workflows/issue-triage-agent.lock.yml
index 40157af1690..a4b2ef527b4 100644
--- a/.github/workflows/issue-triage-agent.lock.yml
+++ b/.github/workflows/issue-triage-agent.lock.yml
@@ -1036,6 +1036,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml
index 6190b2242fc..d3303f4d9c8 100644
--- a/.github/workflows/jsweep.lock.yml
+++ b/.github/workflows/jsweep.lock.yml
@@ -1147,6 +1147,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/layout-spec-maintainer.lock.yml b/.github/workflows/layout-spec-maintainer.lock.yml
index 68e68f215d4..3e06bcbd6f1 100644
--- a/.github/workflows/layout-spec-maintainer.lock.yml
+++ b/.github/workflows/layout-spec-maintainer.lock.yml
@@ -1133,6 +1133,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml
index 722c7d32550..b36c082c9bf 100644
--- a/.github/workflows/lockfile-stats.lock.yml
+++ b/.github/workflows/lockfile-stats.lock.yml
@@ -1118,6 +1118,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml
index fd972055b0f..9c984de9e26 100644
--- a/.github/workflows/mcp-inspector.lock.yml
+++ b/.github/workflows/mcp-inspector.lock.yml
@@ -1829,6 +1829,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml
index 2ff44e94d19..86be37626d4 100644
--- a/.github/workflows/mergefest.lock.yml
+++ b/.github/workflows/mergefest.lock.yml
@@ -1220,6 +1220,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml
index 39f9854de40..f0de34b1e6f 100644
--- a/.github/workflows/notion-issue-summary.lock.yml
+++ b/.github/workflows/notion-issue-summary.lock.yml
@@ -1046,6 +1046,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/org-health-report.lock.yml b/.github/workflows/org-health-report.lock.yml
index d96590dd3a0..5c41ac525d7 100644
--- a/.github/workflows/org-health-report.lock.yml
+++ b/.github/workflows/org-health-report.lock.yml
@@ -1127,6 +1127,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml
index d805e0a0640..02f6896c99e 100644
--- a/.github/workflows/pdf-summary.lock.yml
+++ b/.github/workflows/pdf-summary.lock.yml
@@ -1261,6 +1261,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml
index d41cc20baba..742570b2a99 100644
--- a/.github/workflows/plan.lock.yml
+++ b/.github/workflows/plan.lock.yml
@@ -1185,6 +1185,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/portfolio-analyst.lock.yml b/.github/workflows/portfolio-analyst.lock.yml
index b43e7e0d9e6..7a7642000e5 100644
--- a/.github/workflows/portfolio-analyst.lock.yml
+++ b/.github/workflows/portfolio-analyst.lock.yml
@@ -1209,6 +1209,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml
index 73a3b961f95..acec7df98d6 100644
--- a/.github/workflows/pr-nitpick-reviewer.lock.yml
+++ b/.github/workflows/pr-nitpick-reviewer.lock.yml
@@ -1285,6 +1285,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml
index dddb2494a3d..b36976ba45b 100644
--- a/.github/workflows/pr-triage-agent.lock.yml
+++ b/.github/workflows/pr-triage-agent.lock.yml
@@ -1196,6 +1196,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml
index 4afc6527974..08eb8757a94 100644
--- a/.github/workflows/prompt-clustering-analysis.lock.yml
+++ b/.github/workflows/prompt-clustering-analysis.lock.yml
@@ -1260,6 +1260,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml
index 577ec6ee170..9b041939df3 100644
--- a/.github/workflows/python-data-charts.lock.yml
+++ b/.github/workflows/python-data-charts.lock.yml
@@ -1192,6 +1192,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml
index fa0e71e8140..cf0a1e6df56 100644
--- a/.github/workflows/q.lock.yml
+++ b/.github/workflows/q.lock.yml
@@ -1422,6 +1422,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/refiner.lock.yml b/.github/workflows/refiner.lock.yml
index bc6200896ee..ff9edc9d232 100644
--- a/.github/workflows/refiner.lock.yml
+++ b/.github/workflows/refiner.lock.yml
@@ -1185,6 +1185,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml
index eec0f318242..00b314e1b18 100644
--- a/.github/workflows/release.lock.yml
+++ b/.github/workflows/release.lock.yml
@@ -1415,6 +1415,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/repo-audit-analyzer.lock.yml b/.github/workflows/repo-audit-analyzer.lock.yml
index 70791a2e547..dd0539b8f9f 100644
--- a/.github/workflows/repo-audit-analyzer.lock.yml
+++ b/.github/workflows/repo-audit-analyzer.lock.yml
@@ -1070,6 +1070,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml
index a47364f72f0..1f0a98213eb 100644
--- a/.github/workflows/repo-tree-map.lock.yml
+++ b/.github/workflows/repo-tree-map.lock.yml
@@ -1036,6 +1036,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml
index b9c0c59a09f..61a091cbb38 100644
--- a/.github/workflows/repository-quality-improver.lock.yml
+++ b/.github/workflows/repository-quality-improver.lock.yml
@@ -1081,6 +1081,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml
index b2805986533..3690da9730c 100644
--- a/.github/workflows/research.lock.yml
+++ b/.github/workflows/research.lock.yml
@@ -1069,6 +1069,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml
index b1ef2e328b7..ddad80a1f8c 100644
--- a/.github/workflows/safe-output-health.lock.yml
+++ b/.github/workflows/safe-output-health.lock.yml
@@ -1219,6 +1219,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml
index 93fcd7c8cba..6f24bd136ed 100644
--- a/.github/workflows/schema-consistency-checker.lock.yml
+++ b/.github/workflows/schema-consistency-checker.lock.yml
@@ -1118,6 +1118,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml
index 614fca5179f..a402ec565c0 100644
--- a/.github/workflows/scout.lock.yml
+++ b/.github/workflows/scout.lock.yml
@@ -1451,6 +1451,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/security-alert-burndown.campaign.g.lock.yml b/.github/workflows/security-alert-burndown.campaign.g.lock.yml
index aaa9f443447..6d71089937c 100644
--- a/.github/workflows/security-alert-burndown.campaign.g.lock.yml
+++ b/.github/workflows/security-alert-burndown.campaign.g.lock.yml
@@ -1324,6 +1324,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml
index d2a3a83af88..829ebc7ed77 100644
--- a/.github/workflows/security-compliance.lock.yml
+++ b/.github/workflows/security-compliance.lock.yml
@@ -1175,6 +1175,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/security-review.lock.yml b/.github/workflows/security-review.lock.yml
index 4eae68d43e0..297d4fbc74c 100644
--- a/.github/workflows/security-review.lock.yml
+++ b/.github/workflows/security-review.lock.yml
@@ -1292,6 +1292,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml
index 1880ea02c12..2292b765a39 100644
--- a/.github/workflows/semantic-function-refactor.lock.yml
+++ b/.github/workflows/semantic-function-refactor.lock.yml
@@ -1161,6 +1161,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml
index 32ee19bf390..7b7503ff6f2 100644
--- a/.github/workflows/sergo.lock.yml
+++ b/.github/workflows/sergo.lock.yml
@@ -1169,6 +1169,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/slide-deck-maintainer.lock.yml b/.github/workflows/slide-deck-maintainer.lock.yml
index 8697bb96653..c636b0f9955 100644
--- a/.github/workflows/slide-deck-maintainer.lock.yml
+++ b/.github/workflows/slide-deck-maintainer.lock.yml
@@ -1270,6 +1270,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-agent-all-merged.lock.yml b/.github/workflows/smoke-agent-all-merged.lock.yml
index 9bd03c253d7..77a579e3a5e 100644
--- a/.github/workflows/smoke-agent-all-merged.lock.yml
+++ b/.github/workflows/smoke-agent-all-merged.lock.yml
@@ -1092,6 +1092,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-agent-all-none.lock.yml b/.github/workflows/smoke-agent-all-none.lock.yml
index b1b0705f1a6..603a41bc16c 100644
--- a/.github/workflows/smoke-agent-all-none.lock.yml
+++ b/.github/workflows/smoke-agent-all-none.lock.yml
@@ -1092,6 +1092,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-agent-public-approved.lock.yml b/.github/workflows/smoke-agent-public-approved.lock.yml
index ba609c5202e..afe946b6e15 100644
--- a/.github/workflows/smoke-agent-public-approved.lock.yml
+++ b/.github/workflows/smoke-agent-public-approved.lock.yml
@@ -1140,6 +1140,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-agent-public-none.lock.yml b/.github/workflows/smoke-agent-public-none.lock.yml
index 60616dcb5f0..192fed655a3 100644
--- a/.github/workflows/smoke-agent-public-none.lock.yml
+++ b/.github/workflows/smoke-agent-public-none.lock.yml
@@ -1092,6 +1092,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-agent-scoped-approved.lock.yml b/.github/workflows/smoke-agent-scoped-approved.lock.yml
index 016436cf5a3..5b3916b3091 100644
--- a/.github/workflows/smoke-agent-scoped-approved.lock.yml
+++ b/.github/workflows/smoke-agent-scoped-approved.lock.yml
@@ -1096,6 +1096,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-call-workflow.lock.yml b/.github/workflows/smoke-call-workflow.lock.yml
index b780e0a719c..e92a9565862 100644
--- a/.github/workflows/smoke-call-workflow.lock.yml
+++ b/.github/workflows/smoke-call-workflow.lock.yml
@@ -1071,6 +1071,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml
index bceba229dfd..24dc14c9df5 100644
--- a/.github/workflows/smoke-claude.lock.yml
+++ b/.github/workflows/smoke-claude.lock.yml
@@ -2665,6 +2665,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index 2c73bf9bcab..50053201554 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -1564,6 +1564,7 @@ jobs:
           payload: ${{ steps.process_safe_outputs.outputs.action_add_smoked_label_payload }}
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-copilot-arm.lock.yml b/.github/workflows/smoke-copilot-arm.lock.yml
index ed506a67b24..f036c2e8f6d 100644
--- a/.github/workflows/smoke-copilot-arm.lock.yml
+++ b/.github/workflows/smoke-copilot-arm.lock.yml
@@ -2026,6 +2026,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml
index 81e12c6dfea..ab42a8037c5 100644
--- a/.github/workflows/smoke-copilot.lock.yml
+++ b/.github/workflows/smoke-copilot.lock.yml
@@ -2073,6 +2073,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-create-cross-repo-pr.lock.yml b/.github/workflows/smoke-create-cross-repo-pr.lock.yml
index a851eb6b989..c1b52965643 100644
--- a/.github/workflows/smoke-create-cross-repo-pr.lock.yml
+++ b/.github/workflows/smoke-create-cross-repo-pr.lock.yml
@@ -1257,6 +1257,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-gemini.lock.yml b/.github/workflows/smoke-gemini.lock.yml
index c9fc00abc6c..005bf1a4889 100644
--- a/.github/workflows/smoke-gemini.lock.yml
+++ b/.github/workflows/smoke-gemini.lock.yml
@@ -1327,6 +1327,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-multi-pr.lock.yml b/.github/workflows/smoke-multi-pr.lock.yml
index 0813b6b4506..c0742380a4f 100644
--- a/.github/workflows/smoke-multi-pr.lock.yml
+++ b/.github/workflows/smoke-multi-pr.lock.yml
@@ -1245,6 +1245,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-project.lock.yml b/.github/workflows/smoke-project.lock.yml
index b5d84288a78..bece7c3ae34 100644
--- a/.github/workflows/smoke-project.lock.yml
+++ b/.github/workflows/smoke-project.lock.yml
@@ -1382,6 +1382,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-temporary-id.lock.yml b/.github/workflows/smoke-temporary-id.lock.yml
index d6b7324428b..f361e8221eb 100644
--- a/.github/workflows/smoke-temporary-id.lock.yml
+++ b/.github/workflows/smoke-temporary-id.lock.yml
@@ -1183,6 +1183,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-test-tools.lock.yml b/.github/workflows/smoke-test-tools.lock.yml
index 94f59597c7a..e6782472727 100644
--- a/.github/workflows/smoke-test-tools.lock.yml
+++ b/.github/workflows/smoke-test-tools.lock.yml
@@ -1142,6 +1142,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-update-cross-repo-pr.lock.yml b/.github/workflows/smoke-update-cross-repo-pr.lock.yml
index c05d0a815c7..a4173f8e9d7 100644
--- a/.github/workflows/smoke-update-cross-repo-pr.lock.yml
+++ b/.github/workflows/smoke-update-cross-repo-pr.lock.yml
@@ -1256,6 +1256,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/smoke-workflow-call-with-inputs.lock.yml b/.github/workflows/smoke-workflow-call-with-inputs.lock.yml
index 31712032f44..fe5f00055cf 100644
--- a/.github/workflows/smoke-workflow-call-with-inputs.lock.yml
+++ b/.github/workflows/smoke-workflow-call-with-inputs.lock.yml
@@ -1138,6 +1138,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: ${{ needs.activation.outputs.artifact_prefix }}agent
diff --git a/.github/workflows/smoke-workflow-call.lock.yml b/.github/workflows/smoke-workflow-call.lock.yml
index c60f14c723f..8cc4f49005e 100644
--- a/.github/workflows/smoke-workflow-call.lock.yml
+++ b/.github/workflows/smoke-workflow-call.lock.yml
@@ -1132,6 +1132,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: ${{ needs.activation.outputs.artifact_prefix }}agent
diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml
index 8d4ed10d7d1..2d552c2f2dc 100644
--- a/.github/workflows/stale-repo-identifier.lock.yml
+++ b/.github/workflows/stale-repo-identifier.lock.yml
@@ -1185,6 +1185,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml
index 192c517687d..46304019db6 100644
--- a/.github/workflows/static-analysis-report.lock.yml
+++ b/.github/workflows/static-analysis-report.lock.yml
@@ -1201,6 +1201,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/step-name-alignment.lock.yml b/.github/workflows/step-name-alignment.lock.yml
index 086c4f07be2..7524b8369fa 100644
--- a/.github/workflows/step-name-alignment.lock.yml
+++ b/.github/workflows/step-name-alignment.lock.yml
@@ -1131,6 +1131,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/sub-issue-closer.lock.yml b/.github/workflows/sub-issue-closer.lock.yml
index 3761a47f7d3..82fbfdbf178 100644
--- a/.github/workflows/sub-issue-closer.lock.yml
+++ b/.github/workflows/sub-issue-closer.lock.yml
@@ -1078,6 +1078,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml
index f9e450e69f4..af327bbda26 100644
--- a/.github/workflows/super-linter.lock.yml
+++ b/.github/workflows/super-linter.lock.yml
@@ -1080,6 +1080,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml
index 3e53ef12722..4e3dfd3c001 100644
--- a/.github/workflows/technical-doc-writer.lock.yml
+++ b/.github/workflows/technical-doc-writer.lock.yml
@@ -1460,6 +1460,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/terminal-stylist.lock.yml b/.github/workflows/terminal-stylist.lock.yml
index f9124e6d71a..aef9389f2f9 100644
--- a/.github/workflows/terminal-stylist.lock.yml
+++ b/.github/workflows/terminal-stylist.lock.yml
@@ -1055,6 +1055,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/test-create-pr-error-handling.lock.yml b/.github/workflows/test-create-pr-error-handling.lock.yml
index f902a970c5a..99c9503d021 100644
--- a/.github/workflows/test-create-pr-error-handling.lock.yml
+++ b/.github/workflows/test-create-pr-error-handling.lock.yml
@@ -1166,6 +1166,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/test-dispatcher.lock.yml b/.github/workflows/test-dispatcher.lock.yml
index 0100d839a7b..735a062f009 100644
--- a/.github/workflows/test-dispatcher.lock.yml
+++ b/.github/workflows/test-dispatcher.lock.yml
@@ -1007,6 +1007,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/test-project-url-default.lock.yml b/.github/workflows/test-project-url-default.lock.yml
index 45528477a19..206e2cf7e8b 100644
--- a/.github/workflows/test-project-url-default.lock.yml
+++ b/.github/workflows/test-project-url-default.lock.yml
@@ -1075,6 +1075,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml
index 99ee6e564ab..b7aba2bbb52 100644
--- a/.github/workflows/tidy.lock.yml
+++ b/.github/workflows/tidy.lock.yml
@@ -1286,6 +1286,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml
index 864271590fe..3e9e97a2bbd 100644
--- a/.github/workflows/typist.lock.yml
+++ b/.github/workflows/typist.lock.yml
@@ -1136,6 +1136,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/ubuntu-image-analyzer.lock.yml b/.github/workflows/ubuntu-image-analyzer.lock.yml
index 2b8abae2870..b03ca6a4d92 100644
--- a/.github/workflows/ubuntu-image-analyzer.lock.yml
+++ b/.github/workflows/ubuntu-image-analyzer.lock.yml
@@ -1192,6 +1192,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml
index dbab44b3f52..945f3619497 100644
--- a/.github/workflows/unbloat-docs.lock.yml
+++ b/.github/workflows/unbloat-docs.lock.yml
@@ -1601,6 +1601,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/update-astro.lock.yml b/.github/workflows/update-astro.lock.yml
index c8bcf46274d..0f31138c431 100644
--- a/.github/workflows/update-astro.lock.yml
+++ b/.github/workflows/update-astro.lock.yml
@@ -1202,6 +1202,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml
index d33c3f97ceb..5695e9c7b52 100644
--- a/.github/workflows/video-analyzer.lock.yml
+++ b/.github/workflows/video-analyzer.lock.yml
@@ -1072,6 +1072,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/weekly-blog-post-writer.lock.yml b/.github/workflows/weekly-blog-post-writer.lock.yml
index d85cde0f0d3..6e1ed43a13a 100644
--- a/.github/workflows/weekly-blog-post-writer.lock.yml
+++ b/.github/workflows/weekly-blog-post-writer.lock.yml
@@ -1426,6 +1426,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/weekly-editors-health-check.lock.yml b/.github/workflows/weekly-editors-health-check.lock.yml
index 51dbc7144e7..a06f197a9f1 100644
--- a/.github/workflows/weekly-editors-health-check.lock.yml
+++ b/.github/workflows/weekly-editors-health-check.lock.yml
@@ -1167,6 +1167,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml
index b5470c1885d..91191361e3c 100644
--- a/.github/workflows/weekly-issue-summary.lock.yml
+++ b/.github/workflows/weekly-issue-summary.lock.yml
@@ -1111,6 +1111,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/weekly-safe-outputs-spec-review.lock.yml b/.github/workflows/weekly-safe-outputs-spec-review.lock.yml
index 387fb372008..abff6e05e15 100644
--- a/.github/workflows/weekly-safe-outputs-spec-review.lock.yml
+++ b/.github/workflows/weekly-safe-outputs-spec-review.lock.yml
@@ -1099,6 +1099,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml
index d10b92c1c17..a3705fc3ee8 100644
--- a/.github/workflows/workflow-generator.lock.yml
+++ b/.github/workflows/workflow-generator.lock.yml
@@ -1198,6 +1198,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml
index 0aa72c1ae6d..aa72747537c 100644
--- a/.github/workflows/workflow-health-manager.lock.yml
+++ b/.github/workflows/workflow-health-manager.lock.yml
@@ -1268,6 +1268,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/workflow-normalizer.lock.yml b/.github/workflows/workflow-normalizer.lock.yml
index 0f5c7a0fb07..5288b98ac16 100644
--- a/.github/workflows/workflow-normalizer.lock.yml
+++ b/.github/workflows/workflow-normalizer.lock.yml
@@ -1123,6 +1123,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/.github/workflows/workflow-skill-extractor.lock.yml b/.github/workflows/workflow-skill-extractor.lock.yml
index d97ff3606f2..4c781d9d4c5 100644
--- a/.github/workflows/workflow-skill-extractor.lock.yml
+++ b/.github/workflows/workflow-skill-extractor.lock.yml
@@ -1093,6 +1093,7 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
+        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: agent
diff --git a/pkg/workflow/compiler_safe_outputs_job.go b/pkg/workflow/compiler_safe_outputs_job.go
index 8800b731265..56476a9abe8 100644
--- a/pkg/workflow/compiler_safe_outputs_job.go
+++ b/pkg/workflow/compiler_safe_outputs_job.go
@@ -525,10 +525,14 @@ func buildDetectionSuccessCondition() ConditionNode {
 // The file is written to /tmp/gh-aw/safe-output-items.jsonl so it is merged into the
 // same "agent" artifact that the main job uploads, rather than a separate artifact.
 // prefix is prepended to the artifact name; use empty string for non-workflow_call workflows.
+// continue-on-error is set to true because actions/upload-artifact v4+ returns a 409
+// conflict when a second job tries to upload to an artifact name that already exists;
+// the upload conflict must not fail the safe_outputs job.
 func buildSafeOutputItemsManifestUploadStep(prefix string) []string {
 	return []string{
 		"      - name: Upload safe output items\n",
 		"        if: always()\n",
+		"        continue-on-error: true\n",
 		fmt.Sprintf("        uses: %s\n", GetActionPin("actions/upload-artifact")),
 		"        with:\n",
 		fmt.Sprintf("          name: %sagent\n", prefix),

From 29c9973c63b5239f2d2dd84fca68de844593b1cd Mon Sep 17 00:00:00 2001
From: Peli de Halleux <pelikhan@users.noreply.github.com>
Date: Thu, 19 Mar 2026 10:37:59 -0700
Subject: [PATCH 12/17] Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---
 pkg/workflow/safe_outputs_tools_filtering.go | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/pkg/workflow/safe_outputs_tools_filtering.go b/pkg/workflow/safe_outputs_tools_filtering.go
index 6533dfea767..3190fbdec05 100644
--- a/pkg/workflow/safe_outputs_tools_filtering.go
+++ b/pkg/workflow/safe_outputs_tools_filtering.go
@@ -253,6 +253,12 @@ func generateFilteredToolsJSON(data *WorkflowData, markdownPath string) (string,
 	if len(data.SafeOutputs.Actions) > 0 {
 		safeOutputsConfigLog.Printf("Adding %d custom action tools", len(data.SafeOutputs.Actions))
 
+		// Ensure all configured actions are resolved (action.yml metadata loaded)
+		// before generating tool definitions that depend on ActionDescription/Inputs.
+		if err := resolveAllActions(); err != nil {
+			return "", fmt.Errorf("failed to resolve actions before generating safe-output tools: %w", err)
+		}
+
 		actionNames := make([]string, 0, len(data.SafeOutputs.Actions))
 		for actionName := range data.SafeOutputs.Actions {
 			actionNames = append(actionNames, actionName)

From b3559c92e6f7d0a721c4a92b4c7d537ab6d52508 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 18:02:48 +0000
Subject: [PATCH 13/17] fix: remove stale resolveAllActions() call, add action
 handler tests, clean up comments

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .../js/safe_output_action_handler.test.cjs    | 117 ++++++++++++++++++
 actions/setup/js/safe_output_helpers.test.cjs |  47 ++++++-
 pkg/workflow/safe_outputs_actions.go          |   5 +-
 pkg/workflow/safe_outputs_tools_filtering.go  |  11 +-
 4 files changed, 169 insertions(+), 11 deletions(-)
 create mode 100644 actions/setup/js/safe_output_action_handler.test.cjs

diff --git a/actions/setup/js/safe_output_action_handler.test.cjs b/actions/setup/js/safe_output_action_handler.test.cjs
new file mode 100644
index 00000000000..1b214221a2b
--- /dev/null
+++ b/actions/setup/js/safe_output_action_handler.test.cjs
@@ -0,0 +1,117 @@
+// @ts-check
+
+import { describe, it, expect, beforeEach, vi } from "vitest";
+import { main } from "./safe_output_action_handler.cjs";
+
+describe("safe_output_action_handler", () => {
+  beforeEach(() => {
+    // Provide a mock global `core` matching the @actions/core API surface used by the handler.
+    global.core = {
+      info: vi.fn(),
+      debug: vi.fn(),
+      warning: vi.fn(),
+      error: vi.fn(),
+      setOutput: vi.fn(),
+      setFailed: vi.fn(),
+    };
+  });
+
+  describe("main() — factory", () => {
+    it("should return a function (the handler) when called", async () => {
+      const handler = await main({ action_name: "my_action" });
+      expect(typeof handler).toBe("function");
+    });
+
+    it("should use 'unknown_action' when config is not provided", async () => {
+      const handler = await main();
+      expect(typeof handler).toBe("function");
+    });
+  });
+
+  describe("handler — basic payload export", () => {
+    it("should export the payload as a step output and return success", async () => {
+      const handler = await main({ action_name: "add_label" });
+
+      const message = { type: "add_label", labels: "bug" };
+      const result = await handler(message, {}, new Map());
+
+      expect(result.success).toBe(true);
+      expect(result.action_name).toBe("add_label");
+
+      // 'type' (an INTERNAL_MESSAGE_FIELDS member) must be stripped from the exported payload
+      const exported = JSON.parse(global.core.setOutput.mock.calls[0][1]);
+      expect(exported).toHaveProperty("labels", "bug");
+      expect(exported).not.toHaveProperty("type");
+    });
+
+    it("should use the correct output key format action_<name>_payload", async () => {
+      const handler = await main({ action_name: "my_action" });
+      await handler({ type: "my_action", key: "value" }, {}, new Map());
+
+      expect(global.core.setOutput).toHaveBeenCalledWith("action_my_action_payload", expect.any(String));
+    });
+
+    it("should pass through non-string values without sanitization", async () => {
+      const handler = await main({ action_name: "my_action" });
+      const message = { type: "my_action", count: 42, active: true };
+
+      await handler(message, {}, new Map());
+
+      const exported = JSON.parse(global.core.setOutput.mock.calls[0][1]);
+      expect(exported.count).toBe(42);
+      expect(exported.active).toBe(true);
+    });
+  });
+
+  describe("handler — once-only enforcement", () => {
+    it("should return an error on the second call", async () => {
+      const handler = await main({ action_name: "my_action" });
+      const message = { labels: "bug" };
+
+      const first = await handler(message, {}, new Map());
+      expect(first.success).toBe(true);
+
+      const second = await handler(message, {}, new Map());
+      expect(second.success).toBe(false);
+      expect(second.error).toContain("can only be called once");
+
+      // setOutput should only be called once (for the first invocation)
+      expect(global.core.setOutput).toHaveBeenCalledTimes(1);
+    });
+  });
+
+  describe("handler — INTERNAL_MESSAGE_FIELDS filtering", () => {
+    it("should strip 'type' from the exported payload", async () => {
+      const handler = await main({ action_name: "my_action" });
+      await handler({ type: "my_action", title: "hello" }, {}, new Map());
+
+      const exported = JSON.parse(global.core.setOutput.mock.calls[0][1]);
+      expect(exported).not.toHaveProperty("type");
+      expect(exported).toHaveProperty("title", "hello");
+    });
+  });
+
+  describe("handler — temporaryIdMap substitution", () => {
+    it("should substitute temporary ID references in string values", async () => {
+      const handler = await main({ action_name: "my_action" });
+      // The temporary ID pattern is "#aw_XXXX" (3-12 alphanumeric chars with #aw_ prefix)
+      const temporaryIdMap = new Map([["aw_abc1", { repo: "owner/repo", number: 99 }]]);
+      await handler({ type: "my_action", body: "Fixes #aw_abc1" }, {}, temporaryIdMap);
+
+      const exported = JSON.parse(global.core.setOutput.mock.calls[0][1]);
+      // replaceTemporaryIdReferences should replace '#aw_abc1' with the issue reference
+      expect(exported.body).not.toContain("#aw_abc1");
+    });
+  });
+
+  describe("handler — empty payload", () => {
+    it("should handle an empty message (only internal fields)", async () => {
+      const handler = await main({ action_name: "my_action" });
+      const result = await handler({ type: "my_action" }, {}, new Map());
+
+      expect(result.success).toBe(true);
+      const exported = JSON.parse(global.core.setOutput.mock.calls[0][1]);
+      expect(exported).toEqual({});
+    });
+  });
+});
diff --git a/actions/setup/js/safe_output_helpers.test.cjs b/actions/setup/js/safe_output_helpers.test.cjs
index 6cc63b22912..9e68fe46f9d 100644
--- a/actions/setup/js/safe_output_helpers.test.cjs
+++ b/actions/setup/js/safe_output_helpers.test.cjs
@@ -1,4 +1,4 @@
-import { describe, it, expect, beforeEach } from "vitest";
+import { describe, it, expect, beforeEach, vi } from "vitest";
 
 describe("safe_output_helpers", () => {
   let helpers;
@@ -658,4 +658,49 @@ describe("safe_output_helpers", () => {
       }
     });
   });
+
+  describe("loadCustomSafeOutputActionHandlers", () => {
+    beforeEach(() => {
+      delete process.env.GH_AW_SAFE_OUTPUT_ACTIONS;
+      global.core = {
+        info: vi.fn(),
+        debug: vi.fn(),
+        warning: vi.fn(),
+        error: vi.fn(),
+      };
+    });
+
+    it("should return empty map when GH_AW_SAFE_OUTPUT_ACTIONS is not set", () => {
+      const result = helpers.loadCustomSafeOutputActionHandlers();
+      expect(result).toBeInstanceOf(Map);
+      expect(result.size).toBe(0);
+    });
+
+    it("should parse and return action handlers map from env var", () => {
+      process.env.GH_AW_SAFE_OUTPUT_ACTIONS = JSON.stringify({
+        add_smoked_label: "add_smoked_label",
+        notify_slack: "notify_slack",
+      });
+
+      const result = helpers.loadCustomSafeOutputActionHandlers();
+      expect(result).toBeInstanceOf(Map);
+      expect(result.size).toBe(2);
+      expect(result.get("add_smoked_label")).toBe("add_smoked_label");
+      expect(result.get("notify_slack")).toBe("notify_slack");
+    });
+
+    it("should return empty map when env var contains invalid JSON", () => {
+      process.env.GH_AW_SAFE_OUTPUT_ACTIONS = "not-valid-json";
+      const result = helpers.loadCustomSafeOutputActionHandlers();
+      expect(result).toBeInstanceOf(Map);
+      expect(result.size).toBe(0);
+    });
+
+    it("should return empty map when env var contains empty object", () => {
+      process.env.GH_AW_SAFE_OUTPUT_ACTIONS = JSON.stringify({});
+      const result = helpers.loadCustomSafeOutputActionHandlers();
+      expect(result).toBeInstanceOf(Map);
+      expect(result.size).toBe(0);
+    });
+  });
 });
diff --git a/pkg/workflow/safe_outputs_actions.go b/pkg/workflow/safe_outputs_actions.go
index eb6b1aecabb..9f76c3881c6 100644
--- a/pkg/workflow/safe_outputs_actions.go
+++ b/pkg/workflow/safe_outputs_actions.go
@@ -199,9 +199,6 @@ func (c *Compiler) fetchAndParseActionYAML(actionName string, config *SafeOutput
 	}
 }
 
-// fetchRemoteActionYAML fetches and parses action.yml from a GitHub repository.
-// It tries both action.yml and action.yaml filenames.
-
 // extractSHAFromPinnedRef parses the SHA from a pinned action reference string.
 // The format produced by formatActionReference is "repo@sha # version".
 // Returns the SHA string, or empty string if it cannot be parsed.
@@ -222,6 +219,8 @@ func extractSHAFromPinnedRef(pinned string) string {
 	return ""
 }
 
+// fetchRemoteActionYAML fetches and parses action.yml from a GitHub repository.
+// It tries both action.yml and action.yaml filenames.
 func fetchRemoteActionYAML(repo, subdir, ref string) (*actionYAMLFile, error) {
 	for _, filename := range []string{"action.yml", "action.yaml"} {
 		var contentPath string
diff --git a/pkg/workflow/safe_outputs_tools_filtering.go b/pkg/workflow/safe_outputs_tools_filtering.go
index 3190fbdec05..b8e9a531559 100644
--- a/pkg/workflow/safe_outputs_tools_filtering.go
+++ b/pkg/workflow/safe_outputs_tools_filtering.go
@@ -249,16 +249,13 @@ func generateFilteredToolsJSON(data *WorkflowData, markdownPath string) (string,
 		}
 	}
 
-	// Add custom action tools from SafeOutputs.Actions
+	// Add custom action tools from SafeOutputs.Actions.
+	// Action resolution (action.yml fetch + pinning) is done once in buildJobs before
+	// generateToolsMetaJSON/generateFilteredToolsJSON are called, so Inputs and
+	// ActionDescription are already populated here.
 	if len(data.SafeOutputs.Actions) > 0 {
 		safeOutputsConfigLog.Printf("Adding %d custom action tools", len(data.SafeOutputs.Actions))
 
-		// Ensure all configured actions are resolved (action.yml metadata loaded)
-		// before generating tool definitions that depend on ActionDescription/Inputs.
-		if err := resolveAllActions(); err != nil {
-			return "", fmt.Errorf("failed to resolve actions before generating safe-output tools: %w", err)
-		}
-
 		actionNames := make([]string, 0, len(data.SafeOutputs.Actions))
 		for actionName := range data.SafeOutputs.Actions {
 			actionNames = append(actionNames, actionName)

From 8cc1b77409716b8db59e85a23b74834ce70367a6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 19:15:20 +0000
Subject: [PATCH 14/17] Co-authored-by: pelikhan
 <4175913+pelikhan@users.noreply.github.com>

---
 .github/workflows/smoke-codex.lock.yml | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index 50053201554..854c7b96464 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -450,13 +450,20 @@ jobs:
               {
                 "description": "Add the 'smoked' label to the current pull request (can only be called once)",
                 "inputSchema": {
-                  "additionalProperties": true,
+                  "additionalProperties": false,
                   "properties": {
-                    "payload": {
-                      "description": "JSON-encoded payload to pass to the action",
+                    "labels": {
+                      "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.",
+                      "type": "string"
+                    },
+                    "number": {
+                      "description": "The number of the issue or pull request.",
                       "type": "string"
                     }
                   },
+                  "required": [
+                    "labels"
+                  ],
                   "type": "object"
                 },
                 "name": "add_smoked_label"
@@ -1561,7 +1568,8 @@ jobs:
         env:
           GITHUB_TOKEN: ${{ github.token }}
         with:
-          payload: ${{ steps.process_safe_outputs.outputs.action_add_smoked_label_payload }}
+          labels: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).labels }}
+          number: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).number }}
       - name: Upload safe output items
         if: always()
         continue-on-error: true

From a588b348db67aa867ec8503ef2c02c0c4ab1817f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 20:47:54 +0000
Subject: [PATCH 15/17] test: add integration tests for safe-outputs.actions
 compilation

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 pkg/cli/compile_integration_test.go           | 267 ++++++++++++++++++
 .../test-copilot-safe-output-actions.md       |  23 ++
 2 files changed, 290 insertions(+)
 create mode 100644 pkg/cli/workflows/test-copilot-safe-output-actions.md

diff --git a/pkg/cli/compile_integration_test.go b/pkg/cli/compile_integration_test.go
index b38477598f1..659ea65eeac 100644
--- a/pkg/cli/compile_integration_test.go
+++ b/pkg/cli/compile_integration_test.go
@@ -1127,3 +1127,270 @@ Test workflow to verify actions-lock.json path handling when compiling from subd
 
 	t.Logf("Integration test passed - actions-lock.json created at correct location")
 }
+
+// TestCompileSafeOutputsActions verifies that a workflow with safe-outputs.actions
+// compiles successfully and produces the expected output in the lock file:
+// - GH_AW_SAFE_OUTPUT_ACTIONS env var on the process_safe_outputs step
+// - An injected action step with the correct id and if-condition
+func TestCompileSafeOutputsActions(t *testing.T) {
+	setup := setupIntegrationTest(t)
+	defer setup.cleanup()
+
+	testWorkflow := `---
+name: Test Safe Output Actions
+on:
+  workflow_dispatch:
+permissions:
+  contents: read
+  pull-requests: read
+engine: copilot
+safe-outputs:
+  actions:
+    add-label:
+      uses: actions-ecosystem/action-add-labels@v1
+      description: Add a label to the current PR
+      env:
+        GITHUB_TOKEN: ${{ github.token }}
+---
+
+# Test Safe Output Actions
+
+When done, call add_label with the appropriate label.
+`
+	testWorkflowPath := filepath.Join(setup.workflowsDir, "test-safe-output-actions.md")
+	if err := os.WriteFile(testWorkflowPath, []byte(testWorkflow), 0644); err != nil {
+		t.Fatalf("Failed to write test workflow file: %v", err)
+	}
+
+	cmd := exec.Command(setup.binaryPath, "compile", testWorkflowPath)
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("CLI compile command failed: %v\nOutput: %s", err, string(output))
+	}
+
+	lockFilePath := filepath.Join(setup.workflowsDir, "test-safe-output-actions.lock.yml")
+	lockContent, err := os.ReadFile(lockFilePath)
+	if err != nil {
+		t.Fatalf("Failed to read lock file: %v", err)
+	}
+	lockContentStr := string(lockContent)
+
+	// Verify GH_AW_SAFE_OUTPUT_ACTIONS is emitted on the process_safe_outputs step
+	if !strings.Contains(lockContentStr, "GH_AW_SAFE_OUTPUT_ACTIONS") {
+		t.Errorf("Lock file should contain GH_AW_SAFE_OUTPUT_ACTIONS\nLock file content:\n%s", lockContentStr)
+	}
+
+	// Verify the injected action step id
+	if !strings.Contains(lockContentStr, "id: action_add_label") {
+		t.Errorf("Lock file should contain step 'id: action_add_label'\nLock file content:\n%s", lockContentStr)
+	}
+
+	// Verify the injected action step if-condition
+	if !strings.Contains(lockContentStr, "action_add_label_payload") {
+		t.Errorf("Lock file should contain 'action_add_label_payload' in the step if-condition\nLock file content:\n%s", lockContentStr)
+	}
+
+	// Verify the env block is included in the action step
+	if !strings.Contains(lockContentStr, "GITHUB_TOKEN") {
+		t.Errorf("Lock file should contain GITHUB_TOKEN env var in the action step\nLock file content:\n%s", lockContentStr)
+	}
+
+	// Verify the handler manager step is present (required to process action payloads)
+	if !strings.Contains(lockContentStr, "safe_output_handler_manager.cjs") {
+		t.Errorf("Lock file should contain the safe_output_handler_manager.cjs step\nLock file content:\n%s", lockContentStr)
+	}
+}
+
+// TestCompileSafeOutputsActionsMultiple verifies that multiple actions in safe-outputs.actions
+// all generate separate action steps and all appear in GH_AW_SAFE_OUTPUT_ACTIONS.
+func TestCompileSafeOutputsActionsMultiple(t *testing.T) {
+	setup := setupIntegrationTest(t)
+	defer setup.cleanup()
+
+	testWorkflow := `---
+name: Test Multiple Safe Output Actions
+on:
+  workflow_dispatch:
+permissions:
+  contents: read
+  issues: read
+  pull-requests: read
+engine: copilot
+safe-outputs:
+  actions:
+    add-bug-label:
+      uses: actions-ecosystem/action-add-labels@v1
+      description: Add a bug label
+      env:
+        GITHUB_TOKEN: ${{ github.token }}
+    close-issue:
+      uses: peter-evans/close-issue@v3
+      description: Close the issue
+---
+
+# Test Multiple Safe Output Actions
+
+Call add_bug_label or close_issue as appropriate.
+`
+	testWorkflowPath := filepath.Join(setup.workflowsDir, "test-multi-safe-output-actions.md")
+	if err := os.WriteFile(testWorkflowPath, []byte(testWorkflow), 0644); err != nil {
+		t.Fatalf("Failed to write test workflow file: %v", err)
+	}
+
+	cmd := exec.Command(setup.binaryPath, "compile", testWorkflowPath)
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("CLI compile command failed: %v\nOutput: %s", err, string(output))
+	}
+
+	lockFilePath := filepath.Join(setup.workflowsDir, "test-multi-safe-output-actions.lock.yml")
+	lockContent, err := os.ReadFile(lockFilePath)
+	if err != nil {
+		t.Fatalf("Failed to read lock file: %v", err)
+	}
+	lockContentStr := string(lockContent)
+
+	// Both action steps must be present
+	if !strings.Contains(lockContentStr, "id: action_add_bug_label") {
+		t.Errorf("Lock file should contain step 'id: action_add_bug_label'\nLock file content:\n%s", lockContentStr)
+	}
+	if !strings.Contains(lockContentStr, "id: action_close_issue") {
+		t.Errorf("Lock file should contain step 'id: action_close_issue'\nLock file content:\n%s", lockContentStr)
+	}
+
+	// Both payloads must appear in if-conditions
+	if !strings.Contains(lockContentStr, "action_add_bug_label_payload") {
+		t.Errorf("Lock file should contain 'action_add_bug_label_payload'\nLock file content:\n%s", lockContentStr)
+	}
+	if !strings.Contains(lockContentStr, "action_close_issue_payload") {
+		t.Errorf("Lock file should contain 'action_close_issue_payload'\nLock file content:\n%s", lockContentStr)
+	}
+
+	// GH_AW_SAFE_OUTPUT_ACTIONS must mention both tools
+	if !strings.Contains(lockContentStr, "add_bug_label") {
+		t.Errorf("Lock file should contain 'add_bug_label' in GH_AW_SAFE_OUTPUT_ACTIONS\nLock file content:\n%s", lockContentStr)
+	}
+	if !strings.Contains(lockContentStr, "close_issue") {
+		t.Errorf("Lock file should contain 'close_issue' in GH_AW_SAFE_OUTPUT_ACTIONS\nLock file content:\n%s", lockContentStr)
+	}
+}
+
+// TestCompileSafeOutputsActionsCombinedWithBuiltin verifies that safe-outputs.actions
+// can be used alongside built-in safe-output handlers (add-comment, create-issue, etc.)
+// without compilation errors.
+func TestCompileSafeOutputsActionsCombinedWithBuiltin(t *testing.T) {
+	setup := setupIntegrationTest(t)
+	defer setup.cleanup()
+
+	testWorkflow := `---
+name: Combined Safe Outputs
+on:
+  workflow_dispatch:
+permissions:
+  contents: read
+  issues: read
+  pull-requests: read
+engine: copilot
+safe-outputs:
+  add-comment:
+    max: 1
+  add-labels:
+    allowed: [bug, enhancement]
+  actions:
+    apply-fix:
+      uses: actions-ecosystem/action-add-labels@v1
+      description: Apply fix label
+      env:
+        GITHUB_TOKEN: ${{ github.token }}
+---
+
+# Combined Safe Outputs
+
+Use add_comment, add_labels, or apply_fix as appropriate.
+`
+	testWorkflowPath := filepath.Join(setup.workflowsDir, "test-combined-safe-outputs.md")
+	if err := os.WriteFile(testWorkflowPath, []byte(testWorkflow), 0644); err != nil {
+		t.Fatalf("Failed to write test workflow file: %v", err)
+	}
+
+	cmd := exec.Command(setup.binaryPath, "compile", testWorkflowPath)
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("CLI compile command failed: %v\nOutput: %s", err, string(output))
+	}
+
+	lockFilePath := filepath.Join(setup.workflowsDir, "test-combined-safe-outputs.lock.yml")
+	lockContent, err := os.ReadFile(lockFilePath)
+	if err != nil {
+		t.Fatalf("Failed to read lock file: %v", err)
+	}
+	lockContentStr := string(lockContent)
+
+	// Verify both built-in and action tools are present
+	if !strings.Contains(lockContentStr, "GH_AW_SAFE_OUTPUT_ACTIONS") {
+		t.Errorf("Lock file should contain GH_AW_SAFE_OUTPUT_ACTIONS\nLock file content:\n%s", lockContentStr)
+	}
+	if !strings.Contains(lockContentStr, "id: action_apply_fix") {
+		t.Errorf("Lock file should contain step 'id: action_apply_fix'\nLock file content:\n%s", lockContentStr)
+	}
+	// Verify built-in handler config is still present
+	if !strings.Contains(lockContentStr, "GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG") {
+		t.Errorf("Lock file should contain GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG\nLock file content:\n%s", lockContentStr)
+	}
+}
+
+// TestCompileSafeOutputsActionsOnlyNoBuiltin verifies that a workflow with only
+// safe-outputs.actions (no built-in handlers) still compiles correctly and emits
+// the safe_outputs job.
+func TestCompileSafeOutputsActionsOnlyNoBuiltin(t *testing.T) {
+	setup := setupIntegrationTest(t)
+	defer setup.cleanup()
+
+	testWorkflow := `---
+name: Actions Only Safe Outputs
+on:
+  workflow_dispatch:
+permissions:
+  contents: read
+  pull-requests: read
+engine: copilot
+safe-outputs:
+  actions:
+    pin-pr:
+      uses: actions-ecosystem/action-add-labels@v1
+      description: Pin the PR
+---
+
+# Actions Only Safe Outputs
+
+Call pin_pr to pin the pull request.
+`
+	testWorkflowPath := filepath.Join(setup.workflowsDir, "test-actions-only-safe-outputs.md")
+	if err := os.WriteFile(testWorkflowPath, []byte(testWorkflow), 0644); err != nil {
+		t.Fatalf("Failed to write test workflow file: %v", err)
+	}
+
+	cmd := exec.Command(setup.binaryPath, "compile", testWorkflowPath)
+	output, err := cmd.CombinedOutput()
+	if err != nil {
+		t.Fatalf("CLI compile command failed: %v\nOutput: %s", err, string(output))
+	}
+
+	lockFilePath := filepath.Join(setup.workflowsDir, "test-actions-only-safe-outputs.lock.yml")
+	lockContent, err := os.ReadFile(lockFilePath)
+	if err != nil {
+		t.Fatalf("Failed to read lock file: %v", err)
+	}
+	lockContentStr := string(lockContent)
+
+	// Verify the safe_outputs job is created
+	if !strings.Contains(lockContentStr, "safe_outputs") {
+		t.Errorf("Lock file should contain a 'safe_outputs' job\nLock file content:\n%s", lockContentStr)
+	}
+	if !strings.Contains(lockContentStr, "GH_AW_SAFE_OUTPUT_ACTIONS") {
+		t.Errorf("Lock file should contain GH_AW_SAFE_OUTPUT_ACTIONS\nLock file content:\n%s", lockContentStr)
+	}
+	if !strings.Contains(lockContentStr, "id: action_pin_pr") {
+		t.Errorf("Lock file should contain step 'id: action_pin_pr'\nLock file content:\n%s", lockContentStr)
+	}
+}
diff --git a/pkg/cli/workflows/test-copilot-safe-output-actions.md b/pkg/cli/workflows/test-copilot-safe-output-actions.md
new file mode 100644
index 00000000000..87c1dc8a466
--- /dev/null
+++ b/pkg/cli/workflows/test-copilot-safe-output-actions.md
@@ -0,0 +1,23 @@
+---
+on:
+  workflow_dispatch:
+permissions:
+  contents: read
+  pull-requests: read
+engine: copilot
+safe-outputs:
+  actions:
+    add-smoked-label:
+      uses: actions-ecosystem/action-add-labels@v1
+      description: Add the 'smoked' label to the current pull request
+      env:
+        GITHUB_TOKEN: ${{ github.token }}
+---
+
+# Test Safe Output Actions
+
+This workflow demonstrates `safe-outputs.actions`, which mounts a GitHub Action
+as a once-callable MCP tool.
+
+When done, call `add_smoked_label` with `{"labels": "smoked"}` to add the label
+to the current pull request.

From 31250a12da87f8a4da4676271b6049be045876f7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 20:58:45 +0000
Subject: [PATCH 16/17] chore: merge main and recompile

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .github/workflows/smoke-codex.lock.yml | 16 ++++------------
 1 file changed, 4 insertions(+), 12 deletions(-)

diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index c5bc5638e2b..6b67607ca68 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -450,20 +450,13 @@ jobs:
               {
                 "description": "Add the 'smoked' label to the current pull request (can only be called once)",
                 "inputSchema": {
-                  "additionalProperties": false,
+                  "additionalProperties": true,
                   "properties": {
-                    "labels": {
-                      "description": "The labels' name to be added. Must be separated with line breaks if there're multiple labels.",
-                      "type": "string"
-                    },
-                    "number": {
-                      "description": "The number of the issue or pull request.",
+                    "payload": {
+                      "description": "JSON-encoded payload to pass to the action",
                       "type": "string"
                     }
                   },
-                  "required": [
-                    "labels"
-                  ],
                   "type": "object"
                 },
                 "name": "add_smoked_label"
@@ -1568,8 +1561,7 @@ jobs:
         env:
           GITHUB_TOKEN: ${{ github.token }}
         with:
-          labels: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).labels }}
-          number: ${{ fromJSON(steps.process_safe_outputs.outputs.action_add_smoked_label_payload).number }}
+          payload: ${{ steps.process_safe_outputs.outputs.action_add_smoked_label_payload }}
       - name: Upload safe output items
         if: always()
         continue-on-error: true

From de5f3ce5339c25c3f43587a30b063fe990d6b3e5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 19 Mar 2026 21:55:56 +0000
Subject: [PATCH 17/17] fix: remove unrelated continue-on-error change from
 safe output upload step

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/7c570bd1-3259-455c-a206-db36b9f7d3b6
---
 .github/workflows/agent-performance-analyzer.lock.yml         | 1 -
 .github/workflows/agent-persona-explorer.lock.yml             | 1 -
 .github/workflows/ai-moderator.lock.yml                       | 1 -
 .github/workflows/archie.lock.yml                             | 1 -
 .github/workflows/artifacts-summary.lock.yml                  | 1 -
 .github/workflows/audit-workflows.lock.yml                    | 1 -
 .github/workflows/auto-triage-issues.lock.yml                 | 1 -
 .github/workflows/blog-auditor.lock.yml                       | 1 -
 .github/workflows/bot-detection.lock.yml                      | 1 -
 .github/workflows/brave.lock.yml                              | 1 -
 .github/workflows/breaking-change-checker.lock.yml            | 1 -
 .github/workflows/changeset.lock.yml                          | 1 -
 .github/workflows/ci-coach.lock.yml                           | 1 -
 .github/workflows/ci-doctor.lock.yml                          | 1 -
 .github/workflows/claude-code-user-docs-review.lock.yml       | 1 -
 .github/workflows/cli-consistency-checker.lock.yml            | 1 -
 .github/workflows/cli-version-checker.lock.yml                | 1 -
 .github/workflows/cloclo.lock.yml                             | 1 -
 .github/workflows/code-scanning-fixer.lock.yml                | 1 -
 .github/workflows/code-simplifier.lock.yml                    | 1 -
 .github/workflows/commit-changes-analyzer.lock.yml            | 1 -
 .github/workflows/constraint-solving-potd.lock.yml            | 1 -
 .github/workflows/contribution-check.lock.yml                 | 1 -
 .github/workflows/copilot-agent-analysis.lock.yml             | 1 -
 .github/workflows/copilot-cli-deep-research.lock.yml          | 1 -
 .github/workflows/copilot-pr-merged-report.lock.yml           | 1 -
 .github/workflows/copilot-pr-nlp-analysis.lock.yml            | 1 -
 .github/workflows/copilot-pr-prompt-analysis.lock.yml         | 1 -
 .github/workflows/copilot-session-insights.lock.yml           | 1 -
 .github/workflows/craft.lock.yml                              | 1 -
 .github/workflows/daily-architecture-diagram.lock.yml         | 1 -
 .github/workflows/daily-assign-issue-to-user.lock.yml         | 1 -
 .github/workflows/daily-cli-performance.lock.yml              | 1 -
 .github/workflows/daily-cli-tools-tester.lock.yml             | 1 -
 .github/workflows/daily-code-metrics.lock.yml                 | 1 -
 .github/workflows/daily-compiler-quality.lock.yml             | 1 -
 .github/workflows/daily-copilot-token-report.lock.yml         | 1 -
 .github/workflows/daily-doc-healer.lock.yml                   | 1 -
 .github/workflows/daily-doc-updater.lock.yml                  | 1 -
 .github/workflows/daily-fact.lock.yml                         | 1 -
 .github/workflows/daily-file-diet.lock.yml                    | 1 -
 .github/workflows/daily-firewall-report.lock.yml              | 1 -
 .github/workflows/daily-function-namer.lock.yml               | 1 -
 .github/workflows/daily-issues-report.lock.yml                | 1 -
 .github/workflows/daily-malicious-code-scan.lock.yml          | 1 -
 .github/workflows/daily-mcp-concurrency-analysis.lock.yml     | 1 -
 .github/workflows/daily-multi-device-docs-tester.lock.yml     | 1 -
 .github/workflows/daily-news.lock.yml                         | 1 -
 .github/workflows/daily-observability-report.lock.yml         | 1 -
 .github/workflows/daily-performance-summary.lock.yml          | 1 -
 .github/workflows/daily-regulatory.lock.yml                   | 1 -
 .github/workflows/daily-rendering-scripts-verifier.lock.yml   | 1 -
 .github/workflows/daily-repo-chronicle.lock.yml               | 1 -
 .github/workflows/daily-safe-output-integrator.lock.yml       | 1 -
 .github/workflows/daily-safe-output-optimizer.lock.yml        | 1 -
 .github/workflows/daily-safe-outputs-conformance.lock.yml     | 1 -
 .github/workflows/daily-secrets-analysis.lock.yml             | 1 -
 .github/workflows/daily-security-red-team.lock.yml            | 1 -
 .github/workflows/daily-semgrep-scan.lock.yml                 | 1 -
 .github/workflows/daily-syntax-error-quality.lock.yml         | 1 -
 .github/workflows/daily-team-evolution-insights.lock.yml      | 1 -
 .github/workflows/daily-team-status.lock.yml                  | 1 -
 .github/workflows/daily-testify-uber-super-expert.lock.yml    | 1 -
 .github/workflows/daily-workflow-updater.lock.yml             | 1 -
 .github/workflows/dead-code-remover.lock.yml                  | 1 -
 .github/workflows/deep-report.lock.yml                        | 1 -
 .github/workflows/delight.lock.yml                            | 1 -
 .github/workflows/dependabot-burner.lock.yml                  | 1 -
 .github/workflows/dependabot-go-checker.lock.yml              | 1 -
 .github/workflows/dev-hawk.lock.yml                           | 1 -
 .github/workflows/dev.lock.yml                                | 1 -
 .github/workflows/developer-docs-consolidator.lock.yml        | 1 -
 .github/workflows/dictation-prompt.lock.yml                   | 1 -
 .github/workflows/discussion-task-miner.lock.yml              | 1 -
 .github/workflows/docs-noob-tester.lock.yml                   | 1 -
 .github/workflows/draft-pr-cleanup.lock.yml                   | 1 -
 .github/workflows/duplicate-code-detector.lock.yml            | 1 -
 .github/workflows/example-workflow-analyzer.lock.yml          | 1 -
 .github/workflows/firewall-escape.lock.yml                    | 1 -
 .github/workflows/functional-pragmatist.lock.yml              | 1 -
 .github/workflows/github-mcp-structural-analysis.lock.yml     | 1 -
 .github/workflows/github-mcp-tools-report.lock.yml            | 1 -
 .github/workflows/github-remote-mcp-auth-test.lock.yml        | 1 -
 .github/workflows/glossary-maintainer.lock.yml                | 1 -
 .github/workflows/go-fan.lock.yml                             | 1 -
 .github/workflows/go-logger.lock.yml                          | 1 -
 .github/workflows/go-pattern-detector.lock.yml                | 1 -
 .github/workflows/gpclean.lock.yml                            | 1 -
 .github/workflows/grumpy-reviewer.lock.yml                    | 1 -
 .github/workflows/hourly-ci-cleaner.lock.yml                  | 1 -
 .github/workflows/instructions-janitor.lock.yml               | 1 -
 .github/workflows/issue-arborist.lock.yml                     | 1 -
 .github/workflows/issue-monster.lock.yml                      | 1 -
 .github/workflows/issue-triage-agent.lock.yml                 | 1 -
 .github/workflows/jsweep.lock.yml                             | 1 -
 .github/workflows/layout-spec-maintainer.lock.yml             | 1 -
 .github/workflows/lockfile-stats.lock.yml                     | 1 -
 .github/workflows/mcp-inspector.lock.yml                      | 1 -
 .github/workflows/mergefest.lock.yml                          | 1 -
 .github/workflows/notion-issue-summary.lock.yml               | 1 -
 .github/workflows/org-health-report.lock.yml                  | 1 -
 .github/workflows/pdf-summary.lock.yml                        | 1 -
 .github/workflows/plan.lock.yml                               | 1 -
 .github/workflows/portfolio-analyst.lock.yml                  | 1 -
 .github/workflows/pr-nitpick-reviewer.lock.yml                | 1 -
 .github/workflows/pr-triage-agent.lock.yml                    | 1 -
 .github/workflows/prompt-clustering-analysis.lock.yml         | 1 -
 .github/workflows/python-data-charts.lock.yml                 | 1 -
 .github/workflows/q.lock.yml                                  | 1 -
 .github/workflows/refiner.lock.yml                            | 1 -
 .github/workflows/release.lock.yml                            | 1 -
 .github/workflows/repo-audit-analyzer.lock.yml                | 1 -
 .github/workflows/repo-tree-map.lock.yml                      | 1 -
 .github/workflows/repository-quality-improver.lock.yml        | 1 -
 .github/workflows/research.lock.yml                           | 1 -
 .github/workflows/safe-output-health.lock.yml                 | 1 -
 .github/workflows/schema-consistency-checker.lock.yml         | 1 -
 .github/workflows/scout.lock.yml                              | 1 -
 .github/workflows/security-alert-burndown.campaign.g.lock.yml | 1 -
 .github/workflows/security-compliance.lock.yml                | 1 -
 .github/workflows/security-review.lock.yml                    | 1 -
 .github/workflows/semantic-function-refactor.lock.yml         | 1 -
 .github/workflows/sergo.lock.yml                              | 1 -
 .github/workflows/slide-deck-maintainer.lock.yml              | 1 -
 .github/workflows/smoke-agent-all-merged.lock.yml             | 1 -
 .github/workflows/smoke-agent-all-none.lock.yml               | 1 -
 .github/workflows/smoke-agent-public-approved.lock.yml        | 1 -
 .github/workflows/smoke-agent-public-none.lock.yml            | 1 -
 .github/workflows/smoke-agent-scoped-approved.lock.yml        | 1 -
 .github/workflows/smoke-call-workflow.lock.yml                | 1 -
 .github/workflows/smoke-claude.lock.yml                       | 1 -
 .github/workflows/smoke-codex.lock.yml                        | 1 -
 .github/workflows/smoke-copilot-arm.lock.yml                  | 1 -
 .github/workflows/smoke-copilot.lock.yml                      | 1 -
 .github/workflows/smoke-create-cross-repo-pr.lock.yml         | 1 -
 .github/workflows/smoke-gemini.lock.yml                       | 1 -
 .github/workflows/smoke-multi-pr.lock.yml                     | 1 -
 .github/workflows/smoke-project.lock.yml                      | 1 -
 .github/workflows/smoke-temporary-id.lock.yml                 | 1 -
 .github/workflows/smoke-test-tools.lock.yml                   | 1 -
 .github/workflows/smoke-update-cross-repo-pr.lock.yml         | 1 -
 .github/workflows/smoke-workflow-call-with-inputs.lock.yml    | 1 -
 .github/workflows/smoke-workflow-call.lock.yml                | 1 -
 .github/workflows/stale-repo-identifier.lock.yml              | 1 -
 .github/workflows/static-analysis-report.lock.yml             | 1 -
 .github/workflows/step-name-alignment.lock.yml                | 1 -
 .github/workflows/sub-issue-closer.lock.yml                   | 1 -
 .github/workflows/super-linter.lock.yml                       | 1 -
 .github/workflows/technical-doc-writer.lock.yml               | 1 -
 .github/workflows/terminal-stylist.lock.yml                   | 1 -
 .github/workflows/test-create-pr-error-handling.lock.yml      | 1 -
 .github/workflows/test-dispatcher.lock.yml                    | 1 -
 .github/workflows/test-project-url-default.lock.yml           | 1 -
 .github/workflows/tidy.lock.yml                               | 1 -
 .github/workflows/typist.lock.yml                             | 1 -
 .github/workflows/ubuntu-image-analyzer.lock.yml              | 1 -
 .github/workflows/unbloat-docs.lock.yml                       | 1 -
 .github/workflows/update-astro.lock.yml                       | 1 -
 .github/workflows/video-analyzer.lock.yml                     | 1 -
 .github/workflows/weekly-blog-post-writer.lock.yml            | 1 -
 .github/workflows/weekly-editors-health-check.lock.yml        | 1 -
 .github/workflows/weekly-issue-summary.lock.yml               | 1 -
 .github/workflows/weekly-safe-outputs-spec-review.lock.yml    | 1 -
 .github/workflows/workflow-generator.lock.yml                 | 1 -
 .github/workflows/workflow-health-manager.lock.yml            | 1 -
 .github/workflows/workflow-normalizer.lock.yml                | 1 -
 .github/workflows/workflow-skill-extractor.lock.yml           | 1 -
 pkg/workflow/compiler_safe_outputs_job.go                     | 4 ----
 168 files changed, 171 deletions(-)

diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml
index 8aaf42d1c36..033e618d9ec 100644
--- a/.github/workflows/agent-performance-analyzer.lock.yml
+++ b/.github/workflows/agent-performance-analyzer.lock.yml
@@ -1313,7 +1313,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/agent-persona-explorer.lock.yml b/.github/workflows/agent-persona-explorer.lock.yml
index 05d8a25ea75..95e237f2c04 100644
--- a/.github/workflows/agent-persona-explorer.lock.yml
+++ b/.github/workflows/agent-persona-explorer.lock.yml
@@ -1173,7 +1173,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/ai-moderator.lock.yml b/.github/workflows/ai-moderator.lock.yml
index 497e9d8d436..8ad596aa119 100644
--- a/.github/workflows/ai-moderator.lock.yml
+++ b/.github/workflows/ai-moderator.lock.yml
@@ -1036,7 +1036,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/archie.lock.yml b/.github/workflows/archie.lock.yml
index ff19c9a82dc..65d85aef3fa 100644
--- a/.github/workflows/archie.lock.yml
+++ b/.github/workflows/archie.lock.yml
@@ -1192,7 +1192,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml
index 1c9625d6b5c..6345d383adc 100644
--- a/.github/workflows/artifacts-summary.lock.yml
+++ b/.github/workflows/artifacts-summary.lock.yml
@@ -1035,7 +1035,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml
index 6a4348b04cd..625bd7d07a1 100644
--- a/.github/workflows/audit-workflows.lock.yml
+++ b/.github/workflows/audit-workflows.lock.yml
@@ -1370,7 +1370,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/auto-triage-issues.lock.yml b/.github/workflows/auto-triage-issues.lock.yml
index d4071cad715..030bb514bc3 100644
--- a/.github/workflows/auto-triage-issues.lock.yml
+++ b/.github/workflows/auto-triage-issues.lock.yml
@@ -1134,7 +1134,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml
index 223c3ae8785..5094a933ae0 100644
--- a/.github/workflows/blog-auditor.lock.yml
+++ b/.github/workflows/blog-auditor.lock.yml
@@ -1166,7 +1166,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/bot-detection.lock.yml b/.github/workflows/bot-detection.lock.yml
index 11d04cc4bc7..ee455119016 100644
--- a/.github/workflows/bot-detection.lock.yml
+++ b/.github/workflows/bot-detection.lock.yml
@@ -1791,7 +1791,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/brave.lock.yml b/.github/workflows/brave.lock.yml
index 89d9b8b9df7..793d5979b2e 100644
--- a/.github/workflows/brave.lock.yml
+++ b/.github/workflows/brave.lock.yml
@@ -1177,7 +1177,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/breaking-change-checker.lock.yml b/.github/workflows/breaking-change-checker.lock.yml
index b749ba5f8e0..f012e023444 100644
--- a/.github/workflows/breaking-change-checker.lock.yml
+++ b/.github/workflows/breaking-change-checker.lock.yml
@@ -1131,7 +1131,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml
index b35dd37c052..ebd12dbcb7b 100644
--- a/.github/workflows/changeset.lock.yml
+++ b/.github/workflows/changeset.lock.yml
@@ -1156,7 +1156,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/ci-coach.lock.yml b/.github/workflows/ci-coach.lock.yml
index 746483d94c0..68e905b157e 100644
--- a/.github/workflows/ci-coach.lock.yml
+++ b/.github/workflows/ci-coach.lock.yml
@@ -1178,7 +1178,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml
index f763449d9ce..64a8b2564fd 100644
--- a/.github/workflows/ci-doctor.lock.yml
+++ b/.github/workflows/ci-doctor.lock.yml
@@ -1245,7 +1245,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/claude-code-user-docs-review.lock.yml b/.github/workflows/claude-code-user-docs-review.lock.yml
index dcdc21e5023..b0d22248e89 100644
--- a/.github/workflows/claude-code-user-docs-review.lock.yml
+++ b/.github/workflows/claude-code-user-docs-review.lock.yml
@@ -1127,7 +1127,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml
index 4eeb859ad90..c7fe9e075b4 100644
--- a/.github/workflows/cli-consistency-checker.lock.yml
+++ b/.github/workflows/cli-consistency-checker.lock.yml
@@ -1031,7 +1031,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml
index d5f04aa28fe..e9c265068da 100644
--- a/.github/workflows/cli-version-checker.lock.yml
+++ b/.github/workflows/cli-version-checker.lock.yml
@@ -1132,7 +1132,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml
index a0be1d3d698..dac5b4ff12c 100644
--- a/.github/workflows/cloclo.lock.yml
+++ b/.github/workflows/cloclo.lock.yml
@@ -1583,7 +1583,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml
index 8b785be4cb0..422c7f9b235 100644
--- a/.github/workflows/code-scanning-fixer.lock.yml
+++ b/.github/workflows/code-scanning-fixer.lock.yml
@@ -1282,7 +1282,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/code-simplifier.lock.yml b/.github/workflows/code-simplifier.lock.yml
index 5bf761b4414..ae8788447df 100644
--- a/.github/workflows/code-simplifier.lock.yml
+++ b/.github/workflows/code-simplifier.lock.yml
@@ -1159,7 +1159,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml
index 5cdcd50a61d..4682427c431 100644
--- a/.github/workflows/commit-changes-analyzer.lock.yml
+++ b/.github/workflows/commit-changes-analyzer.lock.yml
@@ -1096,7 +1096,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/constraint-solving-potd.lock.yml b/.github/workflows/constraint-solving-potd.lock.yml
index 708c22ffc95..3c173fb48fb 100644
--- a/.github/workflows/constraint-solving-potd.lock.yml
+++ b/.github/workflows/constraint-solving-potd.lock.yml
@@ -1036,7 +1036,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml
index 918b9fcb508..5b3d902b921 100644
--- a/.github/workflows/contribution-check.lock.yml
+++ b/.github/workflows/contribution-check.lock.yml
@@ -1074,7 +1074,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml
index 1274436648b..d5f09af7494 100644
--- a/.github/workflows/copilot-agent-analysis.lock.yml
+++ b/.github/workflows/copilot-agent-analysis.lock.yml
@@ -1249,7 +1249,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/copilot-cli-deep-research.lock.yml b/.github/workflows/copilot-cli-deep-research.lock.yml
index 777baec5832..fb93afbaeaf 100644
--- a/.github/workflows/copilot-cli-deep-research.lock.yml
+++ b/.github/workflows/copilot-cli-deep-research.lock.yml
@@ -1168,7 +1168,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml
index 7c9396e936b..8c9f1f04876 100644
--- a/.github/workflows/copilot-pr-merged-report.lock.yml
+++ b/.github/workflows/copilot-pr-merged-report.lock.yml
@@ -1212,7 +1212,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
index 8daa0d2e23a..0e28f1b0305 100644
--- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
@@ -1252,7 +1252,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
index 2378b93625d..daab70ed8e2 100644
--- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
@@ -1188,7 +1188,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml
index de448de29f7..3e2ef5aa863 100644
--- a/.github/workflows/copilot-session-insights.lock.yml
+++ b/.github/workflows/copilot-session-insights.lock.yml
@@ -1312,7 +1312,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml
index 12219598abe..ef67803b1b9 100644
--- a/.github/workflows/craft.lock.yml
+++ b/.github/workflows/craft.lock.yml
@@ -1211,7 +1211,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-architecture-diagram.lock.yml b/.github/workflows/daily-architecture-diagram.lock.yml
index 9721a89dfb9..9aac2413cd2 100644
--- a/.github/workflows/daily-architecture-diagram.lock.yml
+++ b/.github/workflows/daily-architecture-diagram.lock.yml
@@ -1152,7 +1152,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-assign-issue-to-user.lock.yml b/.github/workflows/daily-assign-issue-to-user.lock.yml
index 823d4a82bcd..9c8fb658dac 100644
--- a/.github/workflows/daily-assign-issue-to-user.lock.yml
+++ b/.github/workflows/daily-assign-issue-to-user.lock.yml
@@ -1040,7 +1040,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml
index 7a90670151c..5c5d18c5107 100644
--- a/.github/workflows/daily-cli-performance.lock.yml
+++ b/.github/workflows/daily-cli-performance.lock.yml
@@ -1337,7 +1337,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-cli-tools-tester.lock.yml b/.github/workflows/daily-cli-tools-tester.lock.yml
index a486f23424b..f8fa6dffe11 100644
--- a/.github/workflows/daily-cli-tools-tester.lock.yml
+++ b/.github/workflows/daily-cli-tools-tester.lock.yml
@@ -1119,7 +1119,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml
index e76c0467399..ff95f32b567 100644
--- a/.github/workflows/daily-code-metrics.lock.yml
+++ b/.github/workflows/daily-code-metrics.lock.yml
@@ -1291,7 +1291,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-compiler-quality.lock.yml b/.github/workflows/daily-compiler-quality.lock.yml
index dd4227a1c5b..cc4267de0eb 100644
--- a/.github/workflows/daily-compiler-quality.lock.yml
+++ b/.github/workflows/daily-compiler-quality.lock.yml
@@ -1102,7 +1102,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-copilot-token-report.lock.yml b/.github/workflows/daily-copilot-token-report.lock.yml
index f71498b386b..21a9ed06707 100644
--- a/.github/workflows/daily-copilot-token-report.lock.yml
+++ b/.github/workflows/daily-copilot-token-report.lock.yml
@@ -1261,7 +1261,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-doc-healer.lock.yml b/.github/workflows/daily-doc-healer.lock.yml
index 5a60cc8fa2c..a740382d7e7 100644
--- a/.github/workflows/daily-doc-healer.lock.yml
+++ b/.github/workflows/daily-doc-healer.lock.yml
@@ -1406,7 +1406,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml
index a857247cf8d..fbf1cc33755 100644
--- a/.github/workflows/daily-doc-updater.lock.yml
+++ b/.github/workflows/daily-doc-updater.lock.yml
@@ -1354,7 +1354,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-fact.lock.yml b/.github/workflows/daily-fact.lock.yml
index 525ffb36d07..752fefc776f 100644
--- a/.github/workflows/daily-fact.lock.yml
+++ b/.github/workflows/daily-fact.lock.yml
@@ -1053,7 +1053,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml
index 12d9c0c2dc8..d4b66824962 100644
--- a/.github/workflows/daily-file-diet.lock.yml
+++ b/.github/workflows/daily-file-diet.lock.yml
@@ -1141,7 +1141,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml
index 11bd279a727..15d3276363c 100644
--- a/.github/workflows/daily-firewall-report.lock.yml
+++ b/.github/workflows/daily-firewall-report.lock.yml
@@ -1198,7 +1198,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-function-namer.lock.yml b/.github/workflows/daily-function-namer.lock.yml
index 5b62b904550..c48432b95e7 100644
--- a/.github/workflows/daily-function-namer.lock.yml
+++ b/.github/workflows/daily-function-namer.lock.yml
@@ -1169,7 +1169,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml
index 22f7c1a94dc..7b6380997cc 100644
--- a/.github/workflows/daily-issues-report.lock.yml
+++ b/.github/workflows/daily-issues-report.lock.yml
@@ -1176,7 +1176,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-malicious-code-scan.lock.yml b/.github/workflows/daily-malicious-code-scan.lock.yml
index 5816ebbac4d..401add5152c 100644
--- a/.github/workflows/daily-malicious-code-scan.lock.yml
+++ b/.github/workflows/daily-malicious-code-scan.lock.yml
@@ -919,7 +919,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
index 9c3b77cc449..2307c98974d 100644
--- a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
+++ b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
@@ -1137,7 +1137,6 @@ jobs:
             const { main } = require('${{ runner.temp }}/gh-aw/actions/create_agent_session.cjs'); await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml
index e35b6bfa119..98380fd04ad 100644
--- a/.github/workflows/daily-multi-device-docs-tester.lock.yml
+++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml
@@ -1214,7 +1214,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml
index 2db8ed54be7..6a138a941d7 100644
--- a/.github/workflows/daily-news.lock.yml
+++ b/.github/workflows/daily-news.lock.yml
@@ -1329,7 +1329,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-observability-report.lock.yml b/.github/workflows/daily-observability-report.lock.yml
index b9411272f62..c042a4f8339 100644
--- a/.github/workflows/daily-observability-report.lock.yml
+++ b/.github/workflows/daily-observability-report.lock.yml
@@ -1181,7 +1181,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml
index fdc5b3b3213..cc994103396 100644
--- a/.github/workflows/daily-performance-summary.lock.yml
+++ b/.github/workflows/daily-performance-summary.lock.yml
@@ -1640,7 +1640,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-regulatory.lock.yml b/.github/workflows/daily-regulatory.lock.yml
index 7c8b3562411..57dd0d02819 100644
--- a/.github/workflows/daily-regulatory.lock.yml
+++ b/.github/workflows/daily-regulatory.lock.yml
@@ -1561,7 +1561,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-rendering-scripts-verifier.lock.yml b/.github/workflows/daily-rendering-scripts-verifier.lock.yml
index 518ce1e4564..0586d546500 100644
--- a/.github/workflows/daily-rendering-scripts-verifier.lock.yml
+++ b/.github/workflows/daily-rendering-scripts-verifier.lock.yml
@@ -1345,7 +1345,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml
index 10ed612dff4..8d7cfd3cfe7 100644
--- a/.github/workflows/daily-repo-chronicle.lock.yml
+++ b/.github/workflows/daily-repo-chronicle.lock.yml
@@ -1120,7 +1120,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-safe-output-integrator.lock.yml b/.github/workflows/daily-safe-output-integrator.lock.yml
index b59e5daf670..98c9539a9f9 100644
--- a/.github/workflows/daily-safe-output-integrator.lock.yml
+++ b/.github/workflows/daily-safe-output-integrator.lock.yml
@@ -1134,7 +1134,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-safe-output-optimizer.lock.yml b/.github/workflows/daily-safe-output-optimizer.lock.yml
index 22ee644faba..9456e0c3ab0 100644
--- a/.github/workflows/daily-safe-output-optimizer.lock.yml
+++ b/.github/workflows/daily-safe-output-optimizer.lock.yml
@@ -1278,7 +1278,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-safe-outputs-conformance.lock.yml b/.github/workflows/daily-safe-outputs-conformance.lock.yml
index a0cbcbd6d51..ec45d07d92e 100644
--- a/.github/workflows/daily-safe-outputs-conformance.lock.yml
+++ b/.github/workflows/daily-safe-outputs-conformance.lock.yml
@@ -1100,7 +1100,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-secrets-analysis.lock.yml b/.github/workflows/daily-secrets-analysis.lock.yml
index be56acfa9f4..e27c7c2a8a8 100644
--- a/.github/workflows/daily-secrets-analysis.lock.yml
+++ b/.github/workflows/daily-secrets-analysis.lock.yml
@@ -1067,7 +1067,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-security-red-team.lock.yml b/.github/workflows/daily-security-red-team.lock.yml
index b2816e5bbc1..d573d490049 100644
--- a/.github/workflows/daily-security-red-team.lock.yml
+++ b/.github/workflows/daily-security-red-team.lock.yml
@@ -1104,7 +1104,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-semgrep-scan.lock.yml b/.github/workflows/daily-semgrep-scan.lock.yml
index 9dc33e32f79..003809ba902 100644
--- a/.github/workflows/daily-semgrep-scan.lock.yml
+++ b/.github/workflows/daily-semgrep-scan.lock.yml
@@ -1066,7 +1066,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-syntax-error-quality.lock.yml b/.github/workflows/daily-syntax-error-quality.lock.yml
index 94780094843..37fef3c3b47 100644
--- a/.github/workflows/daily-syntax-error-quality.lock.yml
+++ b/.github/workflows/daily-syntax-error-quality.lock.yml
@@ -1077,7 +1077,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-team-evolution-insights.lock.yml b/.github/workflows/daily-team-evolution-insights.lock.yml
index 343aa74c253..cae609fef9d 100644
--- a/.github/workflows/daily-team-evolution-insights.lock.yml
+++ b/.github/workflows/daily-team-evolution-insights.lock.yml
@@ -1098,7 +1098,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-team-status.lock.yml b/.github/workflows/daily-team-status.lock.yml
index 6fdb43780c0..fc0f455dd64 100644
--- a/.github/workflows/daily-team-status.lock.yml
+++ b/.github/workflows/daily-team-status.lock.yml
@@ -1099,7 +1099,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml
index 35d79b4b694..ee33c3fbaa5 100644
--- a/.github/workflows/daily-testify-uber-super-expert.lock.yml
+++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml
@@ -1251,7 +1251,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/daily-workflow-updater.lock.yml b/.github/workflows/daily-workflow-updater.lock.yml
index bacde287cbc..eaa21152524 100644
--- a/.github/workflows/daily-workflow-updater.lock.yml
+++ b/.github/workflows/daily-workflow-updater.lock.yml
@@ -1091,7 +1091,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/dead-code-remover.lock.yml b/.github/workflows/dead-code-remover.lock.yml
index 45b4fa2ff9c..a6059823a5b 100644
--- a/.github/workflows/dead-code-remover.lock.yml
+++ b/.github/workflows/dead-code-remover.lock.yml
@@ -1182,7 +1182,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml
index c3db1469a30..37613264f97 100644
--- a/.github/workflows/deep-report.lock.yml
+++ b/.github/workflows/deep-report.lock.yml
@@ -1340,7 +1340,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/delight.lock.yml b/.github/workflows/delight.lock.yml
index 298633aac98..07cd647ee60 100644
--- a/.github/workflows/delight.lock.yml
+++ b/.github/workflows/delight.lock.yml
@@ -1217,7 +1217,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/dependabot-burner.lock.yml b/.github/workflows/dependabot-burner.lock.yml
index 03c6b1d40bc..e1ac66db36c 100644
--- a/.github/workflows/dependabot-burner.lock.yml
+++ b/.github/workflows/dependabot-burner.lock.yml
@@ -1076,7 +1076,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml
index ab4a1a5b98c..d84249c54c5 100644
--- a/.github/workflows/dependabot-go-checker.lock.yml
+++ b/.github/workflows/dependabot-go-checker.lock.yml
@@ -1062,7 +1062,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/dev-hawk.lock.yml b/.github/workflows/dev-hawk.lock.yml
index e30969b396a..c6cbc625f9a 100644
--- a/.github/workflows/dev-hawk.lock.yml
+++ b/.github/workflows/dev-hawk.lock.yml
@@ -1181,7 +1181,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml
index 1566cb96a55..089479d2622 100644
--- a/.github/workflows/dev.lock.yml
+++ b/.github/workflows/dev.lock.yml
@@ -1028,7 +1028,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml
index 199308fd2e5..73a745dc30d 100644
--- a/.github/workflows/developer-docs-consolidator.lock.yml
+++ b/.github/workflows/developer-docs-consolidator.lock.yml
@@ -1520,7 +1520,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/dictation-prompt.lock.yml b/.github/workflows/dictation-prompt.lock.yml
index d78694945dc..2c9e9738b10 100644
--- a/.github/workflows/dictation-prompt.lock.yml
+++ b/.github/workflows/dictation-prompt.lock.yml
@@ -1238,7 +1238,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/discussion-task-miner.lock.yml b/.github/workflows/discussion-task-miner.lock.yml
index 3a4aa8b2300..84f420f142a 100644
--- a/.github/workflows/discussion-task-miner.lock.yml
+++ b/.github/workflows/discussion-task-miner.lock.yml
@@ -1187,7 +1187,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml
index f63b167b398..a5b3b361941 100644
--- a/.github/workflows/docs-noob-tester.lock.yml
+++ b/.github/workflows/docs-noob-tester.lock.yml
@@ -1082,7 +1082,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/draft-pr-cleanup.lock.yml b/.github/workflows/draft-pr-cleanup.lock.yml
index 8bbe499b09f..de79c4a73ce 100644
--- a/.github/workflows/draft-pr-cleanup.lock.yml
+++ b/.github/workflows/draft-pr-cleanup.lock.yml
@@ -1076,7 +1076,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml
index 6c99109a3ed..f115c83318a 100644
--- a/.github/workflows/duplicate-code-detector.lock.yml
+++ b/.github/workflows/duplicate-code-detector.lock.yml
@@ -1097,7 +1097,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml
index 8183090c3de..c5fef3ddef4 100644
--- a/.github/workflows/example-workflow-analyzer.lock.yml
+++ b/.github/workflows/example-workflow-analyzer.lock.yml
@@ -1164,7 +1164,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/firewall-escape.lock.yml b/.github/workflows/firewall-escape.lock.yml
index 1b00b38e0f4..5c9964c1bc3 100644
--- a/.github/workflows/firewall-escape.lock.yml
+++ b/.github/workflows/firewall-escape.lock.yml
@@ -1259,7 +1259,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/functional-pragmatist.lock.yml b/.github/workflows/functional-pragmatist.lock.yml
index da6a318d34a..e9bef655cc2 100644
--- a/.github/workflows/functional-pragmatist.lock.yml
+++ b/.github/workflows/functional-pragmatist.lock.yml
@@ -1104,7 +1104,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml
index 66b6e0ac9d1..44daa870979 100644
--- a/.github/workflows/github-mcp-structural-analysis.lock.yml
+++ b/.github/workflows/github-mcp-structural-analysis.lock.yml
@@ -1176,7 +1176,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml
index a3eea2e7c00..f621cf09574 100644
--- a/.github/workflows/github-mcp-tools-report.lock.yml
+++ b/.github/workflows/github-mcp-tools-report.lock.yml
@@ -1213,7 +1213,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/github-remote-mcp-auth-test.lock.yml b/.github/workflows/github-remote-mcp-auth-test.lock.yml
index 76874b374a8..cd826867420 100644
--- a/.github/workflows/github-remote-mcp-auth-test.lock.yml
+++ b/.github/workflows/github-remote-mcp-auth-test.lock.yml
@@ -1045,7 +1045,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml
index faf2ab0f828..b69431ccc99 100644
--- a/.github/workflows/glossary-maintainer.lock.yml
+++ b/.github/workflows/glossary-maintainer.lock.yml
@@ -1441,7 +1441,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml
index 55388aabd4f..2fd3a29cacb 100644
--- a/.github/workflows/go-fan.lock.yml
+++ b/.github/workflows/go-fan.lock.yml
@@ -1170,7 +1170,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml
index ee9a2bd6dd8..7febbd9159b 100644
--- a/.github/workflows/go-logger.lock.yml
+++ b/.github/workflows/go-logger.lock.yml
@@ -1373,7 +1373,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml
index 1918ece4a00..9f4d5ead9e9 100644
--- a/.github/workflows/go-pattern-detector.lock.yml
+++ b/.github/workflows/go-pattern-detector.lock.yml
@@ -1163,7 +1163,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/gpclean.lock.yml b/.github/workflows/gpclean.lock.yml
index 8d558aa4292..b27bd04f716 100644
--- a/.github/workflows/gpclean.lock.yml
+++ b/.github/workflows/gpclean.lock.yml
@@ -1072,7 +1072,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml
index 7d061be2930..4695b7cccda 100644
--- a/.github/workflows/grumpy-reviewer.lock.yml
+++ b/.github/workflows/grumpy-reviewer.lock.yml
@@ -1193,7 +1193,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/hourly-ci-cleaner.lock.yml b/.github/workflows/hourly-ci-cleaner.lock.yml
index f6aef522b11..59442fd4dca 100644
--- a/.github/workflows/hourly-ci-cleaner.lock.yml
+++ b/.github/workflows/hourly-ci-cleaner.lock.yml
@@ -1211,7 +1211,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml
index 2ffbab5580f..97505e32cd6 100644
--- a/.github/workflows/instructions-janitor.lock.yml
+++ b/.github/workflows/instructions-janitor.lock.yml
@@ -1194,7 +1194,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml
index e483049e006..da2a4080013 100644
--- a/.github/workflows/issue-arborist.lock.yml
+++ b/.github/workflows/issue-arborist.lock.yml
@@ -1066,7 +1066,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml
index 9310ce4b768..005f8063a69 100644
--- a/.github/workflows/issue-monster.lock.yml
+++ b/.github/workflows/issue-monster.lock.yml
@@ -1802,7 +1802,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/issue-triage-agent.lock.yml b/.github/workflows/issue-triage-agent.lock.yml
index ee784aa6d91..f7bcb8c9371 100644
--- a/.github/workflows/issue-triage-agent.lock.yml
+++ b/.github/workflows/issue-triage-agent.lock.yml
@@ -1012,7 +1012,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml
index e078329a406..680e4124a2c 100644
--- a/.github/workflows/jsweep.lock.yml
+++ b/.github/workflows/jsweep.lock.yml
@@ -1147,7 +1147,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/layout-spec-maintainer.lock.yml b/.github/workflows/layout-spec-maintainer.lock.yml
index b5f130e8f83..6ad3988ac1f 100644
--- a/.github/workflows/layout-spec-maintainer.lock.yml
+++ b/.github/workflows/layout-spec-maintainer.lock.yml
@@ -1133,7 +1133,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml
index 65b0c3f4861..79a1047b682 100644
--- a/.github/workflows/lockfile-stats.lock.yml
+++ b/.github/workflows/lockfile-stats.lock.yml
@@ -1118,7 +1118,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml
index 990cc4c2ab1..f10bfc913a6 100644
--- a/.github/workflows/mcp-inspector.lock.yml
+++ b/.github/workflows/mcp-inspector.lock.yml
@@ -1829,7 +1829,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml
index a92e731b91e..9b74f2ce38a 100644
--- a/.github/workflows/mergefest.lock.yml
+++ b/.github/workflows/mergefest.lock.yml
@@ -1220,7 +1220,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml
index 0be1f510c22..d460aed9714 100644
--- a/.github/workflows/notion-issue-summary.lock.yml
+++ b/.github/workflows/notion-issue-summary.lock.yml
@@ -1046,7 +1046,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/org-health-report.lock.yml b/.github/workflows/org-health-report.lock.yml
index b3cdf624edd..9e6c80721d7 100644
--- a/.github/workflows/org-health-report.lock.yml
+++ b/.github/workflows/org-health-report.lock.yml
@@ -1103,7 +1103,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml
index 8071a7c0390..47871d6435c 100644
--- a/.github/workflows/pdf-summary.lock.yml
+++ b/.github/workflows/pdf-summary.lock.yml
@@ -1261,7 +1261,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml
index c91f9cd0f81..079f78fba47 100644
--- a/.github/workflows/plan.lock.yml
+++ b/.github/workflows/plan.lock.yml
@@ -1185,7 +1185,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/portfolio-analyst.lock.yml b/.github/workflows/portfolio-analyst.lock.yml
index 06cfa8b51d4..2c618c8d932 100644
--- a/.github/workflows/portfolio-analyst.lock.yml
+++ b/.github/workflows/portfolio-analyst.lock.yml
@@ -1209,7 +1209,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml
index 8ad76ca2af7..8d870896403 100644
--- a/.github/workflows/pr-nitpick-reviewer.lock.yml
+++ b/.github/workflows/pr-nitpick-reviewer.lock.yml
@@ -1285,7 +1285,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml
index b6f399541ec..5e9f19c49af 100644
--- a/.github/workflows/pr-triage-agent.lock.yml
+++ b/.github/workflows/pr-triage-agent.lock.yml
@@ -1172,7 +1172,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml
index 8f08d798f6b..198489128f6 100644
--- a/.github/workflows/prompt-clustering-analysis.lock.yml
+++ b/.github/workflows/prompt-clustering-analysis.lock.yml
@@ -1260,7 +1260,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml
index 5566240a3e9..cdcd2a5b308 100644
--- a/.github/workflows/python-data-charts.lock.yml
+++ b/.github/workflows/python-data-charts.lock.yml
@@ -1192,7 +1192,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml
index 63acc91ae67..a17390ef716 100644
--- a/.github/workflows/q.lock.yml
+++ b/.github/workflows/q.lock.yml
@@ -1422,7 +1422,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/refiner.lock.yml b/.github/workflows/refiner.lock.yml
index 3d8cdf3a4ba..aee6a94bc26 100644
--- a/.github/workflows/refiner.lock.yml
+++ b/.github/workflows/refiner.lock.yml
@@ -1161,7 +1161,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml
index 82a5e68c62a..f57f7399a49 100644
--- a/.github/workflows/release.lock.yml
+++ b/.github/workflows/release.lock.yml
@@ -1415,7 +1415,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/repo-audit-analyzer.lock.yml b/.github/workflows/repo-audit-analyzer.lock.yml
index 39e439b2d62..861cff40b9f 100644
--- a/.github/workflows/repo-audit-analyzer.lock.yml
+++ b/.github/workflows/repo-audit-analyzer.lock.yml
@@ -1070,7 +1070,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml
index 3a09fce0796..aa843b6da48 100644
--- a/.github/workflows/repo-tree-map.lock.yml
+++ b/.github/workflows/repo-tree-map.lock.yml
@@ -1036,7 +1036,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml
index 0781364e7ad..5013608ea79 100644
--- a/.github/workflows/repository-quality-improver.lock.yml
+++ b/.github/workflows/repository-quality-improver.lock.yml
@@ -1081,7 +1081,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml
index f39bd9fdd82..53d142349b8 100644
--- a/.github/workflows/research.lock.yml
+++ b/.github/workflows/research.lock.yml
@@ -1069,7 +1069,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml
index f58995ce3b8..bfe4e13fc4e 100644
--- a/.github/workflows/safe-output-health.lock.yml
+++ b/.github/workflows/safe-output-health.lock.yml
@@ -1219,7 +1219,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml
index 6d01a07dac1..e8eebced226 100644
--- a/.github/workflows/schema-consistency-checker.lock.yml
+++ b/.github/workflows/schema-consistency-checker.lock.yml
@@ -1118,7 +1118,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml
index 5c7eb23e1a0..2f8ba78f276 100644
--- a/.github/workflows/scout.lock.yml
+++ b/.github/workflows/scout.lock.yml
@@ -1451,7 +1451,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/security-alert-burndown.campaign.g.lock.yml b/.github/workflows/security-alert-burndown.campaign.g.lock.yml
index df03998b2c9..22766d5b1bc 100644
--- a/.github/workflows/security-alert-burndown.campaign.g.lock.yml
+++ b/.github/workflows/security-alert-burndown.campaign.g.lock.yml
@@ -1324,7 +1324,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml
index 86f96d5bd36..45861528548 100644
--- a/.github/workflows/security-compliance.lock.yml
+++ b/.github/workflows/security-compliance.lock.yml
@@ -1175,7 +1175,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/security-review.lock.yml b/.github/workflows/security-review.lock.yml
index 434c6468f6e..f93507f44c3 100644
--- a/.github/workflows/security-review.lock.yml
+++ b/.github/workflows/security-review.lock.yml
@@ -1292,7 +1292,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml
index 1c2b2cfbbad..d24350a9b5c 100644
--- a/.github/workflows/semantic-function-refactor.lock.yml
+++ b/.github/workflows/semantic-function-refactor.lock.yml
@@ -1161,7 +1161,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml
index 2e757fc635e..128e54bda10 100644
--- a/.github/workflows/sergo.lock.yml
+++ b/.github/workflows/sergo.lock.yml
@@ -1169,7 +1169,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/slide-deck-maintainer.lock.yml b/.github/workflows/slide-deck-maintainer.lock.yml
index f4c76eaf0be..86b67e9d540 100644
--- a/.github/workflows/slide-deck-maintainer.lock.yml
+++ b/.github/workflows/slide-deck-maintainer.lock.yml
@@ -1259,7 +1259,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-agent-all-merged.lock.yml b/.github/workflows/smoke-agent-all-merged.lock.yml
index 5de6c14099a..b2c48b439df 100644
--- a/.github/workflows/smoke-agent-all-merged.lock.yml
+++ b/.github/workflows/smoke-agent-all-merged.lock.yml
@@ -1092,7 +1092,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-agent-all-none.lock.yml b/.github/workflows/smoke-agent-all-none.lock.yml
index 1fc5b6ea3a8..2b5a7763f09 100644
--- a/.github/workflows/smoke-agent-all-none.lock.yml
+++ b/.github/workflows/smoke-agent-all-none.lock.yml
@@ -1092,7 +1092,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-agent-public-approved.lock.yml b/.github/workflows/smoke-agent-public-approved.lock.yml
index bef84783a2d..1f4d5bb66b3 100644
--- a/.github/workflows/smoke-agent-public-approved.lock.yml
+++ b/.github/workflows/smoke-agent-public-approved.lock.yml
@@ -1140,7 +1140,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-agent-public-none.lock.yml b/.github/workflows/smoke-agent-public-none.lock.yml
index 4cd731a9dee..fce82aaa748 100644
--- a/.github/workflows/smoke-agent-public-none.lock.yml
+++ b/.github/workflows/smoke-agent-public-none.lock.yml
@@ -1092,7 +1092,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-agent-scoped-approved.lock.yml b/.github/workflows/smoke-agent-scoped-approved.lock.yml
index 5145bba526e..d6c24073c86 100644
--- a/.github/workflows/smoke-agent-scoped-approved.lock.yml
+++ b/.github/workflows/smoke-agent-scoped-approved.lock.yml
@@ -1096,7 +1096,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-call-workflow.lock.yml b/.github/workflows/smoke-call-workflow.lock.yml
index a23d87c8817..3398bed3019 100644
--- a/.github/workflows/smoke-call-workflow.lock.yml
+++ b/.github/workflows/smoke-call-workflow.lock.yml
@@ -1071,7 +1071,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml
index b96f87ab008..957bbf08fd8 100644
--- a/.github/workflows/smoke-claude.lock.yml
+++ b/.github/workflows/smoke-claude.lock.yml
@@ -2665,7 +2665,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index 6b67607ca68..fbd43c9e6df 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -1564,7 +1564,6 @@ jobs:
           payload: ${{ steps.process_safe_outputs.outputs.action_add_smoked_label_payload }}
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-copilot-arm.lock.yml b/.github/workflows/smoke-copilot-arm.lock.yml
index ba51a432f97..3f22db53ef1 100644
--- a/.github/workflows/smoke-copilot-arm.lock.yml
+++ b/.github/workflows/smoke-copilot-arm.lock.yml
@@ -2026,7 +2026,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml
index 0816c5534bd..7285241531a 100644
--- a/.github/workflows/smoke-copilot.lock.yml
+++ b/.github/workflows/smoke-copilot.lock.yml
@@ -2073,7 +2073,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-create-cross-repo-pr.lock.yml b/.github/workflows/smoke-create-cross-repo-pr.lock.yml
index 642e3754d6f..524675b3c28 100644
--- a/.github/workflows/smoke-create-cross-repo-pr.lock.yml
+++ b/.github/workflows/smoke-create-cross-repo-pr.lock.yml
@@ -1257,7 +1257,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-gemini.lock.yml b/.github/workflows/smoke-gemini.lock.yml
index 2f4e55c239e..7adabe5bc90 100644
--- a/.github/workflows/smoke-gemini.lock.yml
+++ b/.github/workflows/smoke-gemini.lock.yml
@@ -1327,7 +1327,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-multi-pr.lock.yml b/.github/workflows/smoke-multi-pr.lock.yml
index 5edb64cc4b9..532e9147dbc 100644
--- a/.github/workflows/smoke-multi-pr.lock.yml
+++ b/.github/workflows/smoke-multi-pr.lock.yml
@@ -1245,7 +1245,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-project.lock.yml b/.github/workflows/smoke-project.lock.yml
index b9f4db9d7be..338e7d73888 100644
--- a/.github/workflows/smoke-project.lock.yml
+++ b/.github/workflows/smoke-project.lock.yml
@@ -1382,7 +1382,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-temporary-id.lock.yml b/.github/workflows/smoke-temporary-id.lock.yml
index 211338efa02..599fbc3bb4c 100644
--- a/.github/workflows/smoke-temporary-id.lock.yml
+++ b/.github/workflows/smoke-temporary-id.lock.yml
@@ -1183,7 +1183,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-test-tools.lock.yml b/.github/workflows/smoke-test-tools.lock.yml
index 96c3b3eaeb4..e04b7bc4262 100644
--- a/.github/workflows/smoke-test-tools.lock.yml
+++ b/.github/workflows/smoke-test-tools.lock.yml
@@ -1142,7 +1142,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-update-cross-repo-pr.lock.yml b/.github/workflows/smoke-update-cross-repo-pr.lock.yml
index 7aed35068de..c77cdda0198 100644
--- a/.github/workflows/smoke-update-cross-repo-pr.lock.yml
+++ b/.github/workflows/smoke-update-cross-repo-pr.lock.yml
@@ -1256,7 +1256,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/smoke-workflow-call-with-inputs.lock.yml b/.github/workflows/smoke-workflow-call-with-inputs.lock.yml
index 3aef9b88278..3f82bca1912 100644
--- a/.github/workflows/smoke-workflow-call-with-inputs.lock.yml
+++ b/.github/workflows/smoke-workflow-call-with-inputs.lock.yml
@@ -1138,7 +1138,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: ${{ needs.activation.outputs.artifact_prefix }}safe-output-items
diff --git a/.github/workflows/smoke-workflow-call.lock.yml b/.github/workflows/smoke-workflow-call.lock.yml
index 709be08fcaa..96b8d7428ae 100644
--- a/.github/workflows/smoke-workflow-call.lock.yml
+++ b/.github/workflows/smoke-workflow-call.lock.yml
@@ -1132,7 +1132,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: ${{ needs.activation.outputs.artifact_prefix }}safe-output-items
diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml
index c6a1dd75ac4..d60c4f077f5 100644
--- a/.github/workflows/stale-repo-identifier.lock.yml
+++ b/.github/workflows/stale-repo-identifier.lock.yml
@@ -1161,7 +1161,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml
index 9e2c85987f3..37c2a727301 100644
--- a/.github/workflows/static-analysis-report.lock.yml
+++ b/.github/workflows/static-analysis-report.lock.yml
@@ -1201,7 +1201,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/step-name-alignment.lock.yml b/.github/workflows/step-name-alignment.lock.yml
index c1128a54254..37a18528d20 100644
--- a/.github/workflows/step-name-alignment.lock.yml
+++ b/.github/workflows/step-name-alignment.lock.yml
@@ -1131,7 +1131,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/sub-issue-closer.lock.yml b/.github/workflows/sub-issue-closer.lock.yml
index 402b5a7a7b4..f0145a4f2e7 100644
--- a/.github/workflows/sub-issue-closer.lock.yml
+++ b/.github/workflows/sub-issue-closer.lock.yml
@@ -1078,7 +1078,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml
index 4d2b2949ede..9d44361c2a4 100644
--- a/.github/workflows/super-linter.lock.yml
+++ b/.github/workflows/super-linter.lock.yml
@@ -1080,7 +1080,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml
index 6d3f3b07cbc..85070968609 100644
--- a/.github/workflows/technical-doc-writer.lock.yml
+++ b/.github/workflows/technical-doc-writer.lock.yml
@@ -1460,7 +1460,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/terminal-stylist.lock.yml b/.github/workflows/terminal-stylist.lock.yml
index 74bb6cebf8d..123164f45fd 100644
--- a/.github/workflows/terminal-stylist.lock.yml
+++ b/.github/workflows/terminal-stylist.lock.yml
@@ -1055,7 +1055,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/test-create-pr-error-handling.lock.yml b/.github/workflows/test-create-pr-error-handling.lock.yml
index 13997bd7c87..ef1e72399fb 100644
--- a/.github/workflows/test-create-pr-error-handling.lock.yml
+++ b/.github/workflows/test-create-pr-error-handling.lock.yml
@@ -1166,7 +1166,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/test-dispatcher.lock.yml b/.github/workflows/test-dispatcher.lock.yml
index 6b820ae4417..7c87cc34c25 100644
--- a/.github/workflows/test-dispatcher.lock.yml
+++ b/.github/workflows/test-dispatcher.lock.yml
@@ -1007,7 +1007,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/test-project-url-default.lock.yml b/.github/workflows/test-project-url-default.lock.yml
index 60ffcf73419..63706334807 100644
--- a/.github/workflows/test-project-url-default.lock.yml
+++ b/.github/workflows/test-project-url-default.lock.yml
@@ -1075,7 +1075,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml
index 0b9ec758982..5c494791ef9 100644
--- a/.github/workflows/tidy.lock.yml
+++ b/.github/workflows/tidy.lock.yml
@@ -1286,7 +1286,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml
index 5b9560d24ce..366a6f26940 100644
--- a/.github/workflows/typist.lock.yml
+++ b/.github/workflows/typist.lock.yml
@@ -1136,7 +1136,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/ubuntu-image-analyzer.lock.yml b/.github/workflows/ubuntu-image-analyzer.lock.yml
index 0b2c55c8ea8..429538b2fbc 100644
--- a/.github/workflows/ubuntu-image-analyzer.lock.yml
+++ b/.github/workflows/ubuntu-image-analyzer.lock.yml
@@ -1181,7 +1181,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml
index 40e3477500e..402f4642d3f 100644
--- a/.github/workflows/unbloat-docs.lock.yml
+++ b/.github/workflows/unbloat-docs.lock.yml
@@ -1601,7 +1601,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/update-astro.lock.yml b/.github/workflows/update-astro.lock.yml
index 2b0a1c319b8..13391073d6f 100644
--- a/.github/workflows/update-astro.lock.yml
+++ b/.github/workflows/update-astro.lock.yml
@@ -1202,7 +1202,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml
index bac10fdc95d..c43c9cfbe9e 100644
--- a/.github/workflows/video-analyzer.lock.yml
+++ b/.github/workflows/video-analyzer.lock.yml
@@ -1072,7 +1072,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/weekly-blog-post-writer.lock.yml b/.github/workflows/weekly-blog-post-writer.lock.yml
index 682cdd3e2fc..c232479a871 100644
--- a/.github/workflows/weekly-blog-post-writer.lock.yml
+++ b/.github/workflows/weekly-blog-post-writer.lock.yml
@@ -1426,7 +1426,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/weekly-editors-health-check.lock.yml b/.github/workflows/weekly-editors-health-check.lock.yml
index c089e8c39b1..ca3603d33cf 100644
--- a/.github/workflows/weekly-editors-health-check.lock.yml
+++ b/.github/workflows/weekly-editors-health-check.lock.yml
@@ -1167,7 +1167,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml
index 290c6d5513c..b8097d00ad8 100644
--- a/.github/workflows/weekly-issue-summary.lock.yml
+++ b/.github/workflows/weekly-issue-summary.lock.yml
@@ -1087,7 +1087,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/weekly-safe-outputs-spec-review.lock.yml b/.github/workflows/weekly-safe-outputs-spec-review.lock.yml
index 8b9515b6331..549a93fa3ea 100644
--- a/.github/workflows/weekly-safe-outputs-spec-review.lock.yml
+++ b/.github/workflows/weekly-safe-outputs-spec-review.lock.yml
@@ -1075,7 +1075,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml
index 669900fd838..13e4277ff16 100644
--- a/.github/workflows/workflow-generator.lock.yml
+++ b/.github/workflows/workflow-generator.lock.yml
@@ -1174,7 +1174,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml
index a86cb9401b5..fa1bd9a4dd5 100644
--- a/.github/workflows/workflow-health-manager.lock.yml
+++ b/.github/workflows/workflow-health-manager.lock.yml
@@ -1268,7 +1268,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/workflow-normalizer.lock.yml b/.github/workflows/workflow-normalizer.lock.yml
index 004f4a7d539..4edde59aa91 100644
--- a/.github/workflows/workflow-normalizer.lock.yml
+++ b/.github/workflows/workflow-normalizer.lock.yml
@@ -1123,7 +1123,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/.github/workflows/workflow-skill-extractor.lock.yml b/.github/workflows/workflow-skill-extractor.lock.yml
index 47221b39998..c05acb58cce 100644
--- a/.github/workflows/workflow-skill-extractor.lock.yml
+++ b/.github/workflows/workflow-skill-extractor.lock.yml
@@ -1093,7 +1093,6 @@ jobs:
             await main();
       - name: Upload safe output items
         if: always()
-        continue-on-error: true
         uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
         with:
           name: safe-output-items
diff --git a/pkg/workflow/compiler_safe_outputs_job.go b/pkg/workflow/compiler_safe_outputs_job.go
index d6edd10a0e7..147fe78ca0e 100644
--- a/pkg/workflow/compiler_safe_outputs_job.go
+++ b/pkg/workflow/compiler_safe_outputs_job.go
@@ -526,14 +526,10 @@ func buildDetectionSuccessCondition() ConditionNode {
 // "agent" artifact) to avoid a 409 Conflict when both the agent job and safe_outputs job
 // try to upload an artifact with the same name in the same workflow run.
 // prefix is prepended to the artifact name; use empty string for non-workflow_call workflows.
-// continue-on-error is set to true because actions/upload-artifact v4+ returns a 409
-// conflict when a second job tries to upload to an artifact name that already exists;
-// the upload conflict must not fail the safe_outputs job.
 func buildSafeOutputItemsManifestUploadStep(prefix string) []string {
 	return []string{
 		"      - name: Upload safe output items\n",
 		"        if: always()\n",
-		"        continue-on-error: true\n",
 		fmt.Sprintf("        uses: %s\n", GetActionPin("actions/upload-artifact")),
 		"        with:\n",
 		fmt.Sprintf("          name: %s%s\n", prefix, constants.SafeOutputItemsArtifactName),
